Merge lp:~thisfred/ubuntuone-couch/add-tests into lp:~thisfred/ubuntuone-couch/trunk

Proposed by Eric Casteleijn
Status: Merged
Approved by: Eric Casteleijn
Approved revision: 14
Merged at revision: 2
Proposed branch: lp:~thisfred/ubuntuone-couch/add-tests
Merge into: lp:~thisfred/ubuntuone-couch/trunk
Diff against target: 614 lines (+391/-181)
3 files modified
bin/u1couch-query (+32/-181)
tests/test_u1couchquery.py (+164/-0)
u1couch/query.py (+195/-0)
To merge this branch: bzr merge lp:~thisfred/ubuntuone-couch/add-tests
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Alejandro J. Cura (community) Approve
Eric Casteleijn Pending
Review via email: mp+50206@code.launchpad.net

Commit message

Fixed and refactored u1couch-query and added tests

Description of the change

Fixed and refactored u1couch-query and added tests

To post a comment you must log in.
14. By Eric Casteleijn

unchanged: attach bug

Revision history for this message
Alejandro J. Cura (alecu) wrote :

Great to see this being tested and tidied up. Good work!
I've added bug #720917 so the request method returns only json or Exceptions, and thisfred agreed to work on that on a subsequent branch.

review: Approve
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Added bug #720928 to use u1 specific login service.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== renamed file 'bin/ubuntuone-couchdb-query' => 'bin/u1couch-query' (properties changed: -x to +x)
--- bin/ubuntuone-couchdb-query 2011-02-14 20:21:08 +0000
+++ bin/u1couch-query 2011-02-17 19:10:44 +0000
@@ -1,204 +1,55 @@
1#!/usr/bin/python1#!/usr/bin/python
2
3# Copyright 2011 Canonical Ltd.
4#
5# Ubuntu One Couch is free software: you can redistribute it and/or
6# modify it under the terms of the GNU Lesser General Public License
7# version 3 as published by the Free Software Foundation.
8#
9# Ubuntu One Couch is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12# Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public
15# License along with desktopcouch. If not, see
16# <http://www.gnu.org/licenses/>.
17
2"""ubuntuone-couchdb-query: Command line query tool for Ubuntu One CouchDBs"""18"""ubuntuone-couchdb-query: Command line query tool for Ubuntu One CouchDBs"""
319
4# sil@kryogenix.org, 2010-02-13
520
6from optparse import OptionParser21from optparse import OptionParser
7from oauth import oauth
8import gnomekeyring, gobject, httplib2, simplejson, urlparse, cgi, urllib
9
10try:
11 from ubuntu_sso.main import SSOCredentials
12except ImportError:
13 SSOCredentials = None
1422
15import socket23import socket
24from u1couch import query
25
16socket.setdefaulttimeout(5)26socket.setdefaulttimeout(5)
1727
18def get_prod_oauth_token():
19 """Get the token from the keyring"""
20 gobject.set_application_name("Ubuntu One Web API Tool")
21
22 consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
23 items = []
24 items = gnomekeyring.find_items_sync(
25 gnomekeyring.ITEM_GENERIC_SECRET,
26 {'ubuntuone-realm': "https://ubuntuone.com",
27 'oauth-consumer-key': consumer.key})
28 return oauth.OAuthToken.from_string(items[0].secret)
29
30def get_prod_oauth_token(explicit_token_store=None):
31 """Get the token from the keyring"""
32 gobject.set_application_name("Ubuntu One Web API Tool")
33 if (SSOCredentials is not None) and (explicit_token_store in ["sso", None]):
34 creds = SSOCredentials('Ubuntu One')
35 info = creds.find_credentials('Ubuntu One')
36 consumer = oauth.OAuthConsumer(info['consumer_key'],
37 info['consumer_secret'])
38 secret='oauth_token=%s&oauth_token_secret=%s' % (info['token'],
39 info['token_secret'])
40 elif explicit_token_store in ["hammertime", None]:
41 consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
42 items = []
43 items = gnomekeyring.find_items_sync(
44 gnomekeyring.ITEM_GENERIC_SECRET,
45 {'ubuntuone-realm': "https://ubuntuone.com",
46 'oauth-consumer-key': consumer.key})
47 secret = items[0].secret
48 else:
49 raise Exception("Wasn't able to get a token")
50
51 return (oauth.OAuthToken.from_string(secret), consumer)
52
53def get_oauth_request_header(consumer, access_token, http_url, signature_method):
54 """Get an oauth request header given the token and the url"""
55 assert http_url.startswith("https")
56 oauth_request = oauth.OAuthRequest.from_consumer_and_token(
57 http_url=http_url,
58 http_method="GET",
59 oauth_consumer=consumer,
60 token=access_token)
61 oauth_request.sign_request(signature_method, consumer, access_token)
62 return oauth_request.to_header()
63
64def request(urlpath, sigmeth, http_method, request_body, show_tokens,
65 server_override=None, explicit_token_store=None):
66 """Make a request to couchdb.one.ubuntu.com for the user's data.
67
68 The user supplies a urlpath (for example, dbname). We need to actually
69 request https://couchdb.one.ubuntu.com/PREFIX/dbname, and sign it with
70 the user's OAuth token, which must be in the keyring.
71
72 We find the prefix by querying https://one.ubuntu.com/api/account/
73 (see desktopcouch.replication_services.ubuntuone, which does this).
74 """
75
76 # First check that there's a token in the keyring.
77 try:
78 (access_token, consumer) = get_prod_oauth_token(explicit_token_store)
79 except: # bare except, norty
80 raise Exception("Unable to retrieve your Ubuntu One access details "
81 "from the keyring. Try connecting your machine to Ubuntu One.")
82
83 # Set the signature method. This should be HMAC unless you have a jolly
84 # good reason for it to not be.
85 if sigmeth == "PLAINTEXT":
86 signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
87 elif sigmeth == "HMAC_SHA1":
88 signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
89 else:
90 signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
91
92 if show_tokens:
93 print "Using OAuth data:"
94 print "consumer: %s : %s\ntoken: %s" % (
95 consumer.key, consumer.secret, access_token)
96
97 # Look up the user's prefix
98 infourl = "https://one.ubuntu.com/api/account/"
99 oauth_header = get_oauth_request_header(consumer, access_token, infourl, signature_method)
100 client = httplib2.Http()
101 resp, content = client.request(infourl, "GET", headers=oauth_header)
102 if resp['status'] == "200":
103 try:
104 document = simplejson.loads(content)
105 except ValueError:
106 raise Exception("Got unexpected content:\n%s" % content)
107 if "couchdb_root" not in document:
108 raise ValueError("couchdb_root not found in %s" % (document,))
109 if "id" not in document:
110 raise ValueError("id not found in %s" % (document,))
111 COUCH_ROOT = document["couchdb_root"]
112 USERID = document["id"]
113 else:
114 raise ValueError("Error retrieving user data (%s)" % (resp['status'],
115 url))
116
117 # COUCH_ROOT must have all internal slashes escaped
118 schema, netloc, path, params, query, fragment = urlparse.urlparse(COUCH_ROOT)
119 if server_override:
120 netloc = server_override
121 path = "/" + urllib.quote(path[1:], safe="") # don't escape the first /
122 COUCH_ROOT = urlparse.urlunparse((schema, netloc, path, params, query, fragment))
123
124 # Now use COUCH_ROOT and the specified user urlpath to get data
125 if urlpath == "_all_dbs":
126 couch_url = "%s%s" % (COUCH_ROOT, urlpath)
127 couch_url = urlparse.urlunparse((schema, netloc, "_all_dbs", None, "user_id=%s" % USERID, None))
128 else:
129 couch_url = "%s%%2F%s" % (COUCH_ROOT, urlpath)
130 schema, netloc, path, params, query, fragment = urlparse.urlparse(couch_url)
131 querystr_as_dict = dict(cgi.parse_qsl(query))
132 oauth_request = oauth.OAuthRequest.from_consumer_and_token(
133 http_url=couch_url,
134 http_method=http_method,
135 oauth_consumer=consumer,
136 token=access_token,
137 parameters=querystr_as_dict)
138 oauth_request.sign_request(signature_method, consumer, access_token)
139 failed = 0
140 #print "Connecting to effective Couch URL", oauth_request.to_url()
141 #print "Connecting to actual Couch URL (with OAuth header)", couch_url
142 #print oauth_request.to_header(), querystr_as_dict
143 while 1:
144 try:
145 resp, content = client.request(couch_url, http_method,
146 headers=oauth_request.to_header(), body=request_body)
147 break
148 except IOError:
149 failed += 1
150 if failed > 0:
151 if failed == 1:
152 s = ""
153 else:
154 s = "s"
155 print "(request failed %s time%s)" % (failed, s)
156 if resp['status'] == "200":
157 try:
158 return simplejson.loads(content)
159 except:
160 print "(data returned from CouchDB was invalid JSON)"
161 return content
162 elif resp['status'] == "400":
163 print "The server could not parse the oauth token:\n%s" % content
164 elif resp['status'] == "401":
165 print "Access Denied"
166 print "Content:"
167 return content
168 else:
169 return (
170 "There was a problem processing the request:\nstatus:%s, response:"
171 " %r" % (resp['status'], content))
172
17328
174if __name__ == "__main__":29if __name__ == "__main__":
175 parser = OptionParser(usage="prog [options] urlpath")30 PARSER = OptionParser(usage="prog [options] urlpath")
176 parser.add_option("--oauth-signature-method", dest="sigmeth",31 PARSER.add_option("--oauth-signature-method", dest="sigmeth",
177 default="HMAC_SHA1",32 default="HMAC_SHA1",
178 help="OAuth signature method to use (PLAINTEXT or "33 help="OAuth signature method to use (PLAINTEXT or "
179 "HMAC_SHA1)")34 "HMAC_SHA1)")
180 parser.add_option("--http-method", dest="http_method",35 PARSER.add_option("--http-method", dest="http_method",
181 default="GET",36 default="GET",
182 help="HTTP method to use")37 help="HTTP method to use")
183 parser.add_option("--body", dest="body",38 PARSER.add_option("--body", dest="body",
184 default=None,39 default=None,
185 help="HTTP request body")40 help="HTTP request body")
186 parser.add_option("--show-tokens", dest="show_tokens",41 PARSER.add_option("--show-tokens", dest="show_tokens",
187 default=False,42 default=False,
188 help="Show the OAuth tokens we're using")43 help="Show the OAuth tokens we're using")
189 parser.add_option("--server-override", dest="server_override",44 PARSER.add_option("--server-override", dest="server_override",
190 default=None,45 default=None,
191 help="Use a different server")46 help="Use a different server")
192 parser.add_option("--explicit-token-store", dest="explicit_token_store",
193 default=None,
194 help="Explicitly choose a token store (sso or hammertime)")
19547
196 (options, args) = parser.parse_args()48 (OPTIONS, ARGS) = PARSER.parse_args()
197 if len(args) != 1:49 if len(ARGS) != 1:
198 parser.error("You must specify a urlpath (e.g., a dbname)")50 PARSER.error("You must specify a urlpath (e.g., a dbname)")
199 print request(51 print query.request(
200 urlpath=args[0], sigmeth=options.sigmeth,52 urlpath=ARGS[0], sig_meth=OPTIONS.sigmeth,
201 http_method=options.http_method, request_body=options.body,53 http_method=OPTIONS.http_method, request_body=OPTIONS.body,
202 show_tokens=options.show_tokens, 54 show_tokens=OPTIONS.show_tokens,
203 server_override=options.server_override,55 server_override=OPTIONS.server_override)
204 explicit_token_store=options.explicit_token_store)
20556
=== renamed file 'bin/ubuntuone-sign-uri' => 'bin/ubuntuone-sign-uri.py' (properties changed: -x to +x)
=== added directory 'tests'
=== added file 'tests/__init__.py'
=== added file 'tests/test_u1couchquery.py'
--- tests/test_u1couchquery.py 1970-01-01 00:00:00 +0000
+++ tests/test_u1couchquery.py 2011-02-17 19:10:44 +0000
@@ -0,0 +1,164 @@
1# Copyright 2011 Canonical Ltd.
2#
3# Ubuntu One Couch is free software: you can redistribute it and/or
4# modify it under the terms of the GNU Lesser General Public License
5# version 3 as published by the Free Software Foundation.
6#
7# Ubuntu One Couch is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10# Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser General Public
13# License along with desktopcouch. If not, see
14# <http://www.gnu.org/licenses/>.
15
16"""Tests for u1couch.query."""
17
18from twisted.trial.unittest import TestCase
19from mocker import Mocker, ANY
20
21import ubuntu_sso
22import json
23from oauth import oauth
24from u1couch import query
25
26CONSUMER_KEY = u'this_consumer_key'
27CONSUMER_SECRET = u'sssssh!'
28TOKEN_KEY = u'tokentokentoken'
29TOKEN_SECRET = u'ssssssshhhhhh!'
30
31CONSUMER = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
32TOKEN = oauth.OAuthToken(TOKEN_KEY, TOKEN_SECRET)
33
34URL = 'https://example.com'
35
36
37class QueryTestCase(TestCase):
38 """Test case for u1couch.query."""
39
40 def setUp(self):
41 self.mocker = Mocker()
42
43 def tearDown(self):
44 self.mocker.restore()
45 self.mocker.verify()
46
47 def test_get_oauth_request_header(self):
48 """Test get_oauth_request_header returns correct headers."""
49 fake_headers = {'Authorization':
50 'OAuth realm="", oauth_nonce="39941541", '
51 'oauth_timestamp="1297958903", '
52 'oauth_consumer_key="this_consumer_key", '
53 'oauth_signature_method="HMAC-SHA1", oauth_version="1.0", '
54 'oauth_token="tokentokentoken", '
55 'oauth_signature="TNIfersCweWluuuJW%2FT%2FbW9IHD0%3D"'}
56 mock_oauth = self.mocker.replace("oauth.oauth")
57 mock_oauth.OAuthRequest # pylint: disable=W0104
58 MockOAuthRequest = self.mocker.mock() # pylint: disable=C0103
59 self.mocker.result(MockOAuthRequest)
60 MockOAuthRequest.from_consumer_and_token(
61 http_url=URL, http_method='GET', oauth_consumer=CONSUMER,
62 token=TOKEN)
63 oauth_request = self.mocker.mock()
64 self.mocker.result(oauth_request)
65 oauth_request.sign_request(query.HMAC_SHA1, CONSUMER, TOKEN)
66 oauth_request.to_header()
67 self.mocker.result(fake_headers)
68 self.mocker.replay()
69 headers = query.get_oauth_request_header(CONSUMER, TOKEN, URL)
70 self.assertEquals(fake_headers, headers)
71
72 def test_get_oauth_request_header_http(self):
73 """Test get_oauth_request_header fails on http urls."""
74 self.assertRaises(
75 AssertionError, query.get_oauth_request_header, CONSUMER, TOKEN,
76 'http://example.com')
77
78 def test_get_oauth_data(self):
79 """Test get_oauth_data returns proper oauth data."""
80 self.patch(query, "undbusify", lambda x: x)
81 dbus = self.mocker.replace("dbus")
82 self.mocker.replace("dbus.mainloop.glib.DBusGMainLoop")
83 bus = dbus.SessionBus()
84 bus.get_object(
85 ubuntu_sso.DBUS_BUS_NAME, ubuntu_sso.DBUS_CRED_PATH,
86 follow_name_owner_changes=True)
87 mock_proxy = self.mocker.mock()
88 self.mocker.result(mock_proxy)
89 mock_proxy.find_credentials(query.APP_NAME)
90 self.mocker.result({
91 u'token': TOKEN_KEY,
92 u'token_secret': TOKEN_SECRET,
93 u'consumer_secret': CONSUMER_SECRET,
94 u'consumer_key': CONSUMER_KEY})
95 self.mocker.replay()
96 oauth_data = query.get_oauth_data()
97 self.assertEquals(CONSUMER_KEY, oauth_data['consumer_key'])
98 self.assertEquals(CONSUMER_SECRET, oauth_data['consumer_secret'])
99 self.assertEquals(TOKEN_KEY, oauth_data['token'])
100 self.assertEquals(TOKEN_SECRET, oauth_data['token_secret'])
101
102 def test_get_prod_oauth_token(self):
103 """Test get_prod_oauth_token returns token and consumer."""
104 info = {
105 u'token': TOKEN_KEY,
106 u'token_secret': TOKEN_SECRET,
107 u'consumer_secret': CONSUMER_SECRET,
108 u'consumer_key': CONSUMER_KEY}
109 token, consumer = query.get_prod_oauth_token(info)
110 self.assertEquals(TOKEN_KEY, token.key)
111 self.assertEquals(TOKEN_SECRET, token.secret)
112 self.assertEquals(CONSUMER_KEY, consumer.key)
113 self.assertEquals(CONSUMER_SECRET, consumer.secret)
114
115 def test_get_user_info(self):
116 """Test get_user_info parses json correctly."""
117 mock_header = self.mocker.mock()
118 Http = self.mocker.replace("httplib2.Http") # pylint: disable=C0103
119 http = Http()
120 http.request(URL, "GET", headers=mock_header)
121 self.mocker.result((
122 {'status': '200'},
123 '{"couchdb_root": "https://couchdb.one.ubuntu.com/u/abc/def/1337",'
124 ' "id": 1337}'))
125 self.mocker.replay()
126 user_id, root = query.get_user_info(URL, mock_header)
127 self.assertEquals(1337, user_id)
128 self.assertEquals(
129 "https://couchdb.one.ubuntu.com/u/abc/def/1337", root)
130
131 def test_request(self):
132 """Test a full request."""
133 fake_result = {
134 'committed_update_seq': 654,
135 'compact_running': False,
136 'db_name': 'u/abc/def/1337/contacts',
137 'disk_format_version': 5,
138 'disk_size': 929892,
139 'doc_count': 626,
140 'doc_del_count': 1,
141 'instance_start_time': '1297965776474824',
142 'purge_seq': 0,
143 'update_seq': 654}
144 fake_json = json.dumps(fake_result)
145 get_oauth_data = self.mocker.replace('u1couch.query.get_oauth_data')
146 get_oauth_data()
147 self.mocker.result({
148 u'token': TOKEN_KEY,
149 u'token_secret': TOKEN_SECRET,
150 u'consumer_secret': CONSUMER_SECRET,
151 u'consumer_key': CONSUMER_KEY})
152 get_user_info = self.mocker.replace('u1couch.query.get_user_info')
153 get_user_info('https://one.ubuntu.com/api/account/', ANY)
154 self.mocker.result((
155 1337, "https://couchdb.one.ubuntu.com/u/abc/def/1337"))
156 Http = self.mocker.replace("httplib2.Http") # pylint: disable=C0103
157 http = Http()
158 http.request(
159 'https://couchdb.one.ubuntu.com/u%2Fabc%2Fdef%2F1337%2Fcontacts',
160 'GET', headers=ANY, body=None)
161 self.mocker.result(({'status': '200'}, fake_json))
162 self.mocker.replay()
163 result = query.request('contacts')
164 self.assertEquals(fake_result, result)
0165
=== added directory 'u1couch'
=== added file 'u1couch/__init__.py'
=== added file 'u1couch/query.py'
--- u1couch/query.py 1970-01-01 00:00:00 +0000
+++ u1couch/query.py 2011-02-17 19:10:44 +0000
@@ -0,0 +1,195 @@
1# Copyright 2011 Canonical Ltd.
2#
3# Ubuntu One Couch is free software: you can redistribute it and/or
4# modify it under the terms of the GNU Lesser General Public License
5# version 3 as published by the Free Software Foundation.
6#
7# Ubuntu One Couch is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10# Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser General Public
13# License along with desktopcouch. If not, see
14# <http://www.gnu.org/licenses/>.
15
16"""Small library for talking to Ubuntu One CouchDBs."""
17
18import cgi
19import dbus
20from dbus.mainloop.glib import DBusGMainLoop
21import logging
22import httplib2
23import json
24import urllib
25import urlparse
26from time import sleep
27from oauth import oauth
28
29import ubuntu_sso
30
31APP_NAME = "Ubuntu One"
32HMAC_SHA1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
33
34
35def undbusify(value):
36 """Convert dbus types back to native types."""
37 for singleton in (None, True, False):
38 if value == singleton:
39 return singleton
40 for val_type in (long, int, float, complex,
41 unicode, str,
42 list, tuple, dict, set):
43 if isinstance(value, val_type):
44 return val_type(value)
45 raise TypeError(value)
46
47
48def get_oauth_data():
49 """Information needed to replicate to a server."""
50
51 DBusGMainLoop(set_as_default=True)
52
53 bus = dbus.SessionBus()
54 proxy = bus.get_object(
55 ubuntu_sso.DBUS_BUS_NAME, ubuntu_sso.DBUS_CRED_PATH,
56 follow_name_owner_changes=True)
57 logging.info(
58 'get_oauth_data: asking for credentials to Ubuntu SSO. App name: %s',
59 APP_NAME)
60 oauth_data = dict(
61 (undbusify(k), undbusify(v)) for k, v in
62 proxy.find_credentials(APP_NAME).iteritems())
63 logging.info(
64 'Got credentials from Ubuntu SSO. Non emtpy credentials? %s',
65 len(oauth_data) > 0)
66 return oauth_data
67
68
69def get_prod_oauth_token(info):
70 """Get the token from the keyring."""
71 consumer = oauth.OAuthConsumer(
72 info['consumer_key'], info['consumer_secret'])
73 secret = 'oauth_token=%s&oauth_token_secret=%s' % (
74 info['token'], info['token_secret'])
75
76 return (oauth.OAuthToken.from_string(secret), consumer)
77
78
79def get_oauth_request_header(consumer, access_token, http_url,
80 signature_method=HMAC_SHA1):
81 """Get an oauth request header given the token and the url."""
82 assert http_url.startswith("https")
83 oauth_request = oauth.OAuthRequest.from_consumer_and_token(
84 http_url=http_url,
85 http_method="GET",
86 oauth_consumer=consumer,
87 token=access_token)
88 oauth_request.sign_request(signature_method, consumer, access_token)
89 return oauth_request.to_header()
90
91
92def get_user_info(info_url, oauth_header):
93 """Look up the user's user id and prefix."""
94 http = httplib2.Http()
95 resp, content = http.request(info_url, "GET", headers=oauth_header)
96 if resp['status'] not in ("200", "201"):
97 raise ValueError(
98 "Error retrieving user data (%s, %s)" % resp['status'])
99 try:
100 document = json.loads(content)
101 except ValueError:
102 raise Exception("Got unexpected content:\n%s" % content)
103 if "couchdb_root" not in document:
104 raise ValueError("couchdb_root not found in %s" % (document,))
105 if "id" not in document:
106 raise ValueError("id not found in %s" % (document,))
107 return document["id"], document["couchdb_root"]
108
109
110def request(urlpath, sig_meth='HMAC_SHA1', http_method='GET',
111 request_body=None, show_tokens=False, server_override=None,
112 access_token=None, consumer=None):
113 """Make a request to couchdb.one.ubuntu.com for the user's data.
114
115 The user supplies a urlpath (for example, dbname). We need to actually
116 request https://couchdb.one.ubuntu.com/PREFIX/dbname, and sign it with
117 the user's OAuth token.
118
119 We find the prefix by querying https://one.ubuntu.com/api/account/
120 (see desktopcouch.replication_services.ubuntuone, which does this).
121
122 """
123 if access_token is None:
124 info = get_oauth_data()
125 (access_token, consumer) = get_prod_oauth_token(info)
126 # Set the signature method. This should be HMAC unless you have a jolly
127 # good reason for it to not be.
128 if sig_meth == "PLAINTEXT":
129 signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
130 else:
131 signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
132 if show_tokens:
133 print "Using OAuth data:"
134 print "consumer: %s : %s\ntoken: %s" % (
135 consumer.key, consumer.secret, access_token)
136
137 info_url = "https://one.ubuntu.com/api/account/"
138 oauth_header = get_oauth_request_header(
139 consumer, access_token, info_url, signature_method)
140
141 userid, couch_root = get_user_info(info_url, oauth_header)
142 schema, netloc, path, params, query, fragment = urlparse.urlparse(
143 couch_root)
144 if server_override:
145 netloc = server_override
146 # Don't escape the first /
147 path = "/" + urllib.quote(path[1:], safe="")
148 couch_root = urlparse.urlunparse((
149 schema, netloc, path, params, query, fragment))
150
151 # Now use COUCH_ROOT and the specified user urlpath to get data
152 if urlpath == "_all_dbs":
153 couch_url = "%s%s" % (couch_root, urlpath)
154 couch_url = urlparse.urlunparse((
155 schema, netloc, "_all_dbs", None, "user_id=%s" % userid, None))
156 else:
157 couch_url = "%s%%2F%s" % (couch_root, urlpath)
158 schema, netloc, path, params, query, fragment = urlparse.urlparse(
159 couch_url)
160 querystr_as_dict = dict(cgi.parse_qsl(query))
161 oauth_request = oauth.OAuthRequest.from_consumer_and_token(
162 http_url=couch_url,
163 http_method=http_method,
164 oauth_consumer=consumer,
165 token=access_token,
166 parameters=querystr_as_dict)
167 oauth_request.sign_request(signature_method, consumer, access_token)
168 failed = 0
169 while True:
170 try:
171 http = httplib2.Http()
172 print couch_url, http_method
173 resp, content = http.request(couch_url, http_method,
174 headers=oauth_request.to_header(), body=request_body)
175 break
176 except IOError:
177 sleep(2 ** failed) # 1, 2, 4, 8, 16
178 failed += 1
179 if failed > 5:
180 break
181 if failed:
182 print "(request failed %s time%s)" % (
183 failed, "s" if failed > 1 else "")
184 if resp['status'] in ("200", "201"):
185 return json.loads(content)
186 elif resp['status'] == "400":
187 print "The server could not parse the oauth token:\n%s" % content
188 elif resp['status'] == "401":
189 print "Access Denied"
190 print "Content:"
191 return content
192 else:
193 return (
194 "There was a problem processing the request:\nstatus:%s, response:"
195 " %r" % (resp['status'], content))

Subscribers

People subscribed via source and target branches

to all changes: