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
1=== renamed file 'bin/ubuntuone-couchdb-query' => 'bin/u1couch-query' (properties changed: -x to +x)
2--- bin/ubuntuone-couchdb-query 2011-02-14 20:21:08 +0000
3+++ bin/u1couch-query 2011-02-17 19:10:44 +0000
4@@ -1,204 +1,55 @@
5 #!/usr/bin/python
6+
7+# Copyright 2011 Canonical Ltd.
8+#
9+# Ubuntu One Couch is free software: you can redistribute it and/or
10+# modify it under the terms of the GNU Lesser General Public License
11+# version 3 as published by the Free Software Foundation.
12+#
13+# Ubuntu One Couch is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+# Lesser General Public License for more details.
17+#
18+# You should have received a copy of the GNU Lesser General Public
19+# License along with desktopcouch. If not, see
20+# <http://www.gnu.org/licenses/>.
21+
22 """ubuntuone-couchdb-query: Command line query tool for Ubuntu One CouchDBs"""
23
24-# sil@kryogenix.org, 2010-02-13
25
26 from optparse import OptionParser
27-from oauth import oauth
28-import gnomekeyring, gobject, httplib2, simplejson, urlparse, cgi, urllib
29-
30-try:
31- from ubuntu_sso.main import SSOCredentials
32-except ImportError:
33- SSOCredentials = None
34
35 import socket
36+from u1couch import query
37+
38 socket.setdefaulttimeout(5)
39
40-def get_prod_oauth_token():
41- """Get the token from the keyring"""
42- gobject.set_application_name("Ubuntu One Web API Tool")
43-
44- consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
45- items = []
46- items = gnomekeyring.find_items_sync(
47- gnomekeyring.ITEM_GENERIC_SECRET,
48- {'ubuntuone-realm': "https://ubuntuone.com",
49- 'oauth-consumer-key': consumer.key})
50- return oauth.OAuthToken.from_string(items[0].secret)
51-
52-def get_prod_oauth_token(explicit_token_store=None):
53- """Get the token from the keyring"""
54- gobject.set_application_name("Ubuntu One Web API Tool")
55- if (SSOCredentials is not None) and (explicit_token_store in ["sso", None]):
56- creds = SSOCredentials('Ubuntu One')
57- info = creds.find_credentials('Ubuntu One')
58- consumer = oauth.OAuthConsumer(info['consumer_key'],
59- info['consumer_secret'])
60- secret='oauth_token=%s&oauth_token_secret=%s' % (info['token'],
61- info['token_secret'])
62- elif explicit_token_store in ["hammertime", None]:
63- consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
64- items = []
65- items = gnomekeyring.find_items_sync(
66- gnomekeyring.ITEM_GENERIC_SECRET,
67- {'ubuntuone-realm': "https://ubuntuone.com",
68- 'oauth-consumer-key': consumer.key})
69- secret = items[0].secret
70- else:
71- raise Exception("Wasn't able to get a token")
72-
73- return (oauth.OAuthToken.from_string(secret), consumer)
74-
75-def get_oauth_request_header(consumer, access_token, http_url, signature_method):
76- """Get an oauth request header given the token and the url"""
77- assert http_url.startswith("https")
78- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
79- http_url=http_url,
80- http_method="GET",
81- oauth_consumer=consumer,
82- token=access_token)
83- oauth_request.sign_request(signature_method, consumer, access_token)
84- return oauth_request.to_header()
85-
86-def request(urlpath, sigmeth, http_method, request_body, show_tokens,
87- server_override=None, explicit_token_store=None):
88- """Make a request to couchdb.one.ubuntu.com for the user's data.
89-
90- The user supplies a urlpath (for example, dbname). We need to actually
91- request https://couchdb.one.ubuntu.com/PREFIX/dbname, and sign it with
92- the user's OAuth token, which must be in the keyring.
93-
94- We find the prefix by querying https://one.ubuntu.com/api/account/
95- (see desktopcouch.replication_services.ubuntuone, which does this).
96- """
97-
98- # First check that there's a token in the keyring.
99- try:
100- (access_token, consumer) = get_prod_oauth_token(explicit_token_store)
101- except: # bare except, norty
102- raise Exception("Unable to retrieve your Ubuntu One access details "
103- "from the keyring. Try connecting your machine to Ubuntu One.")
104-
105- # Set the signature method. This should be HMAC unless you have a jolly
106- # good reason for it to not be.
107- if sigmeth == "PLAINTEXT":
108- signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
109- elif sigmeth == "HMAC_SHA1":
110- signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
111- else:
112- signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
113-
114- if show_tokens:
115- print "Using OAuth data:"
116- print "consumer: %s : %s\ntoken: %s" % (
117- consumer.key, consumer.secret, access_token)
118-
119- # Look up the user's prefix
120- infourl = "https://one.ubuntu.com/api/account/"
121- oauth_header = get_oauth_request_header(consumer, access_token, infourl, signature_method)
122- client = httplib2.Http()
123- resp, content = client.request(infourl, "GET", headers=oauth_header)
124- if resp['status'] == "200":
125- try:
126- document = simplejson.loads(content)
127- except ValueError:
128- raise Exception("Got unexpected content:\n%s" % content)
129- if "couchdb_root" not in document:
130- raise ValueError("couchdb_root not found in %s" % (document,))
131- if "id" not in document:
132- raise ValueError("id not found in %s" % (document,))
133- COUCH_ROOT = document["couchdb_root"]
134- USERID = document["id"]
135- else:
136- raise ValueError("Error retrieving user data (%s)" % (resp['status'],
137- url))
138-
139- # COUCH_ROOT must have all internal slashes escaped
140- schema, netloc, path, params, query, fragment = urlparse.urlparse(COUCH_ROOT)
141- if server_override:
142- netloc = server_override
143- path = "/" + urllib.quote(path[1:], safe="") # don't escape the first /
144- COUCH_ROOT = urlparse.urlunparse((schema, netloc, path, params, query, fragment))
145-
146- # Now use COUCH_ROOT and the specified user urlpath to get data
147- if urlpath == "_all_dbs":
148- couch_url = "%s%s" % (COUCH_ROOT, urlpath)
149- couch_url = urlparse.urlunparse((schema, netloc, "_all_dbs", None, "user_id=%s" % USERID, None))
150- else:
151- couch_url = "%s%%2F%s" % (COUCH_ROOT, urlpath)
152- schema, netloc, path, params, query, fragment = urlparse.urlparse(couch_url)
153- querystr_as_dict = dict(cgi.parse_qsl(query))
154- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
155- http_url=couch_url,
156- http_method=http_method,
157- oauth_consumer=consumer,
158- token=access_token,
159- parameters=querystr_as_dict)
160- oauth_request.sign_request(signature_method, consumer, access_token)
161- failed = 0
162- #print "Connecting to effective Couch URL", oauth_request.to_url()
163- #print "Connecting to actual Couch URL (with OAuth header)", couch_url
164- #print oauth_request.to_header(), querystr_as_dict
165- while 1:
166- try:
167- resp, content = client.request(couch_url, http_method,
168- headers=oauth_request.to_header(), body=request_body)
169- break
170- except IOError:
171- failed += 1
172- if failed > 0:
173- if failed == 1:
174- s = ""
175- else:
176- s = "s"
177- print "(request failed %s time%s)" % (failed, s)
178- if resp['status'] == "200":
179- try:
180- return simplejson.loads(content)
181- except:
182- print "(data returned from CouchDB was invalid JSON)"
183- return content
184- elif resp['status'] == "400":
185- print "The server could not parse the oauth token:\n%s" % content
186- elif resp['status'] == "401":
187- print "Access Denied"
188- print "Content:"
189- return content
190- else:
191- return (
192- "There was a problem processing the request:\nstatus:%s, response:"
193- " %r" % (resp['status'], content))
194-
195
196 if __name__ == "__main__":
197- parser = OptionParser(usage="prog [options] urlpath")
198- parser.add_option("--oauth-signature-method", dest="sigmeth",
199+ PARSER = OptionParser(usage="prog [options] urlpath")
200+ PARSER.add_option("--oauth-signature-method", dest="sigmeth",
201 default="HMAC_SHA1",
202 help="OAuth signature method to use (PLAINTEXT or "
203 "HMAC_SHA1)")
204- parser.add_option("--http-method", dest="http_method",
205+ PARSER.add_option("--http-method", dest="http_method",
206 default="GET",
207 help="HTTP method to use")
208- parser.add_option("--body", dest="body",
209+ PARSER.add_option("--body", dest="body",
210 default=None,
211 help="HTTP request body")
212- parser.add_option("--show-tokens", dest="show_tokens",
213+ PARSER.add_option("--show-tokens", dest="show_tokens",
214 default=False,
215 help="Show the OAuth tokens we're using")
216- parser.add_option("--server-override", dest="server_override",
217+ PARSER.add_option("--server-override", dest="server_override",
218 default=None,
219 help="Use a different server")
220- parser.add_option("--explicit-token-store", dest="explicit_token_store",
221- default=None,
222- help="Explicitly choose a token store (sso or hammertime)")
223
224- (options, args) = parser.parse_args()
225- if len(args) != 1:
226- parser.error("You must specify a urlpath (e.g., a dbname)")
227- print request(
228- urlpath=args[0], sigmeth=options.sigmeth,
229- http_method=options.http_method, request_body=options.body,
230- show_tokens=options.show_tokens,
231- server_override=options.server_override,
232- explicit_token_store=options.explicit_token_store)
233+ (OPTIONS, ARGS) = PARSER.parse_args()
234+ if len(ARGS) != 1:
235+ PARSER.error("You must specify a urlpath (e.g., a dbname)")
236+ print query.request(
237+ urlpath=ARGS[0], sig_meth=OPTIONS.sigmeth,
238+ http_method=OPTIONS.http_method, request_body=OPTIONS.body,
239+ show_tokens=OPTIONS.show_tokens,
240+ server_override=OPTIONS.server_override)
241
242=== renamed file 'bin/ubuntuone-sign-uri' => 'bin/ubuntuone-sign-uri.py' (properties changed: -x to +x)
243=== added directory 'tests'
244=== added file 'tests/__init__.py'
245=== added file 'tests/test_u1couchquery.py'
246--- tests/test_u1couchquery.py 1970-01-01 00:00:00 +0000
247+++ tests/test_u1couchquery.py 2011-02-17 19:10:44 +0000
248@@ -0,0 +1,164 @@
249+# Copyright 2011 Canonical Ltd.
250+#
251+# Ubuntu One Couch is free software: you can redistribute it and/or
252+# modify it under the terms of the GNU Lesser General Public License
253+# version 3 as published by the Free Software Foundation.
254+#
255+# Ubuntu One Couch is distributed in the hope that it will be useful,
256+# but WITHOUT ANY WARRANTY; without even the implied warranty of
257+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
258+# Lesser General Public License for more details.
259+#
260+# You should have received a copy of the GNU Lesser General Public
261+# License along with desktopcouch. If not, see
262+# <http://www.gnu.org/licenses/>.
263+
264+"""Tests for u1couch.query."""
265+
266+from twisted.trial.unittest import TestCase
267+from mocker import Mocker, ANY
268+
269+import ubuntu_sso
270+import json
271+from oauth import oauth
272+from u1couch import query
273+
274+CONSUMER_KEY = u'this_consumer_key'
275+CONSUMER_SECRET = u'sssssh!'
276+TOKEN_KEY = u'tokentokentoken'
277+TOKEN_SECRET = u'ssssssshhhhhh!'
278+
279+CONSUMER = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
280+TOKEN = oauth.OAuthToken(TOKEN_KEY, TOKEN_SECRET)
281+
282+URL = 'https://example.com'
283+
284+
285+class QueryTestCase(TestCase):
286+ """Test case for u1couch.query."""
287+
288+ def setUp(self):
289+ self.mocker = Mocker()
290+
291+ def tearDown(self):
292+ self.mocker.restore()
293+ self.mocker.verify()
294+
295+ def test_get_oauth_request_header(self):
296+ """Test get_oauth_request_header returns correct headers."""
297+ fake_headers = {'Authorization':
298+ 'OAuth realm="", oauth_nonce="39941541", '
299+ 'oauth_timestamp="1297958903", '
300+ 'oauth_consumer_key="this_consumer_key", '
301+ 'oauth_signature_method="HMAC-SHA1", oauth_version="1.0", '
302+ 'oauth_token="tokentokentoken", '
303+ 'oauth_signature="TNIfersCweWluuuJW%2FT%2FbW9IHD0%3D"'}
304+ mock_oauth = self.mocker.replace("oauth.oauth")
305+ mock_oauth.OAuthRequest # pylint: disable=W0104
306+ MockOAuthRequest = self.mocker.mock() # pylint: disable=C0103
307+ self.mocker.result(MockOAuthRequest)
308+ MockOAuthRequest.from_consumer_and_token(
309+ http_url=URL, http_method='GET', oauth_consumer=CONSUMER,
310+ token=TOKEN)
311+ oauth_request = self.mocker.mock()
312+ self.mocker.result(oauth_request)
313+ oauth_request.sign_request(query.HMAC_SHA1, CONSUMER, TOKEN)
314+ oauth_request.to_header()
315+ self.mocker.result(fake_headers)
316+ self.mocker.replay()
317+ headers = query.get_oauth_request_header(CONSUMER, TOKEN, URL)
318+ self.assertEquals(fake_headers, headers)
319+
320+ def test_get_oauth_request_header_http(self):
321+ """Test get_oauth_request_header fails on http urls."""
322+ self.assertRaises(
323+ AssertionError, query.get_oauth_request_header, CONSUMER, TOKEN,
324+ 'http://example.com')
325+
326+ def test_get_oauth_data(self):
327+ """Test get_oauth_data returns proper oauth data."""
328+ self.patch(query, "undbusify", lambda x: x)
329+ dbus = self.mocker.replace("dbus")
330+ self.mocker.replace("dbus.mainloop.glib.DBusGMainLoop")
331+ bus = dbus.SessionBus()
332+ bus.get_object(
333+ ubuntu_sso.DBUS_BUS_NAME, ubuntu_sso.DBUS_CRED_PATH,
334+ follow_name_owner_changes=True)
335+ mock_proxy = self.mocker.mock()
336+ self.mocker.result(mock_proxy)
337+ mock_proxy.find_credentials(query.APP_NAME)
338+ self.mocker.result({
339+ u'token': TOKEN_KEY,
340+ u'token_secret': TOKEN_SECRET,
341+ u'consumer_secret': CONSUMER_SECRET,
342+ u'consumer_key': CONSUMER_KEY})
343+ self.mocker.replay()
344+ oauth_data = query.get_oauth_data()
345+ self.assertEquals(CONSUMER_KEY, oauth_data['consumer_key'])
346+ self.assertEquals(CONSUMER_SECRET, oauth_data['consumer_secret'])
347+ self.assertEquals(TOKEN_KEY, oauth_data['token'])
348+ self.assertEquals(TOKEN_SECRET, oauth_data['token_secret'])
349+
350+ def test_get_prod_oauth_token(self):
351+ """Test get_prod_oauth_token returns token and consumer."""
352+ info = {
353+ u'token': TOKEN_KEY,
354+ u'token_secret': TOKEN_SECRET,
355+ u'consumer_secret': CONSUMER_SECRET,
356+ u'consumer_key': CONSUMER_KEY}
357+ token, consumer = query.get_prod_oauth_token(info)
358+ self.assertEquals(TOKEN_KEY, token.key)
359+ self.assertEquals(TOKEN_SECRET, token.secret)
360+ self.assertEquals(CONSUMER_KEY, consumer.key)
361+ self.assertEquals(CONSUMER_SECRET, consumer.secret)
362+
363+ def test_get_user_info(self):
364+ """Test get_user_info parses json correctly."""
365+ mock_header = self.mocker.mock()
366+ Http = self.mocker.replace("httplib2.Http") # pylint: disable=C0103
367+ http = Http()
368+ http.request(URL, "GET", headers=mock_header)
369+ self.mocker.result((
370+ {'status': '200'},
371+ '{"couchdb_root": "https://couchdb.one.ubuntu.com/u/abc/def/1337",'
372+ ' "id": 1337}'))
373+ self.mocker.replay()
374+ user_id, root = query.get_user_info(URL, mock_header)
375+ self.assertEquals(1337, user_id)
376+ self.assertEquals(
377+ "https://couchdb.one.ubuntu.com/u/abc/def/1337", root)
378+
379+ def test_request(self):
380+ """Test a full request."""
381+ fake_result = {
382+ 'committed_update_seq': 654,
383+ 'compact_running': False,
384+ 'db_name': 'u/abc/def/1337/contacts',
385+ 'disk_format_version': 5,
386+ 'disk_size': 929892,
387+ 'doc_count': 626,
388+ 'doc_del_count': 1,
389+ 'instance_start_time': '1297965776474824',
390+ 'purge_seq': 0,
391+ 'update_seq': 654}
392+ fake_json = json.dumps(fake_result)
393+ get_oauth_data = self.mocker.replace('u1couch.query.get_oauth_data')
394+ get_oauth_data()
395+ self.mocker.result({
396+ u'token': TOKEN_KEY,
397+ u'token_secret': TOKEN_SECRET,
398+ u'consumer_secret': CONSUMER_SECRET,
399+ u'consumer_key': CONSUMER_KEY})
400+ get_user_info = self.mocker.replace('u1couch.query.get_user_info')
401+ get_user_info('https://one.ubuntu.com/api/account/', ANY)
402+ self.mocker.result((
403+ 1337, "https://couchdb.one.ubuntu.com/u/abc/def/1337"))
404+ Http = self.mocker.replace("httplib2.Http") # pylint: disable=C0103
405+ http = Http()
406+ http.request(
407+ 'https://couchdb.one.ubuntu.com/u%2Fabc%2Fdef%2F1337%2Fcontacts',
408+ 'GET', headers=ANY, body=None)
409+ self.mocker.result(({'status': '200'}, fake_json))
410+ self.mocker.replay()
411+ result = query.request('contacts')
412+ self.assertEquals(fake_result, result)
413
414=== added directory 'u1couch'
415=== added file 'u1couch/__init__.py'
416=== added file 'u1couch/query.py'
417--- u1couch/query.py 1970-01-01 00:00:00 +0000
418+++ u1couch/query.py 2011-02-17 19:10:44 +0000
419@@ -0,0 +1,195 @@
420+# Copyright 2011 Canonical Ltd.
421+#
422+# Ubuntu One Couch is free software: you can redistribute it and/or
423+# modify it under the terms of the GNU Lesser General Public License
424+# version 3 as published by the Free Software Foundation.
425+#
426+# Ubuntu One Couch is distributed in the hope that it will be useful,
427+# but WITHOUT ANY WARRANTY; without even the implied warranty of
428+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
429+# Lesser General Public License for more details.
430+#
431+# You should have received a copy of the GNU Lesser General Public
432+# License along with desktopcouch. If not, see
433+# <http://www.gnu.org/licenses/>.
434+
435+"""Small library for talking to Ubuntu One CouchDBs."""
436+
437+import cgi
438+import dbus
439+from dbus.mainloop.glib import DBusGMainLoop
440+import logging
441+import httplib2
442+import json
443+import urllib
444+import urlparse
445+from time import sleep
446+from oauth import oauth
447+
448+import ubuntu_sso
449+
450+APP_NAME = "Ubuntu One"
451+HMAC_SHA1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
452+
453+
454+def undbusify(value):
455+ """Convert dbus types back to native types."""
456+ for singleton in (None, True, False):
457+ if value == singleton:
458+ return singleton
459+ for val_type in (long, int, float, complex,
460+ unicode, str,
461+ list, tuple, dict, set):
462+ if isinstance(value, val_type):
463+ return val_type(value)
464+ raise TypeError(value)
465+
466+
467+def get_oauth_data():
468+ """Information needed to replicate to a server."""
469+
470+ DBusGMainLoop(set_as_default=True)
471+
472+ bus = dbus.SessionBus()
473+ proxy = bus.get_object(
474+ ubuntu_sso.DBUS_BUS_NAME, ubuntu_sso.DBUS_CRED_PATH,
475+ follow_name_owner_changes=True)
476+ logging.info(
477+ 'get_oauth_data: asking for credentials to Ubuntu SSO. App name: %s',
478+ APP_NAME)
479+ oauth_data = dict(
480+ (undbusify(k), undbusify(v)) for k, v in
481+ proxy.find_credentials(APP_NAME).iteritems())
482+ logging.info(
483+ 'Got credentials from Ubuntu SSO. Non emtpy credentials? %s',
484+ len(oauth_data) > 0)
485+ return oauth_data
486+
487+
488+def get_prod_oauth_token(info):
489+ """Get the token from the keyring."""
490+ consumer = oauth.OAuthConsumer(
491+ info['consumer_key'], info['consumer_secret'])
492+ secret = 'oauth_token=%s&oauth_token_secret=%s' % (
493+ info['token'], info['token_secret'])
494+
495+ return (oauth.OAuthToken.from_string(secret), consumer)
496+
497+
498+def get_oauth_request_header(consumer, access_token, http_url,
499+ signature_method=HMAC_SHA1):
500+ """Get an oauth request header given the token and the url."""
501+ assert http_url.startswith("https")
502+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(
503+ http_url=http_url,
504+ http_method="GET",
505+ oauth_consumer=consumer,
506+ token=access_token)
507+ oauth_request.sign_request(signature_method, consumer, access_token)
508+ return oauth_request.to_header()
509+
510+
511+def get_user_info(info_url, oauth_header):
512+ """Look up the user's user id and prefix."""
513+ http = httplib2.Http()
514+ resp, content = http.request(info_url, "GET", headers=oauth_header)
515+ if resp['status'] not in ("200", "201"):
516+ raise ValueError(
517+ "Error retrieving user data (%s, %s)" % resp['status'])
518+ try:
519+ document = json.loads(content)
520+ except ValueError:
521+ raise Exception("Got unexpected content:\n%s" % content)
522+ if "couchdb_root" not in document:
523+ raise ValueError("couchdb_root not found in %s" % (document,))
524+ if "id" not in document:
525+ raise ValueError("id not found in %s" % (document,))
526+ return document["id"], document["couchdb_root"]
527+
528+
529+def request(urlpath, sig_meth='HMAC_SHA1', http_method='GET',
530+ request_body=None, show_tokens=False, server_override=None,
531+ access_token=None, consumer=None):
532+ """Make a request to couchdb.one.ubuntu.com for the user's data.
533+
534+ The user supplies a urlpath (for example, dbname). We need to actually
535+ request https://couchdb.one.ubuntu.com/PREFIX/dbname, and sign it with
536+ the user's OAuth token.
537+
538+ We find the prefix by querying https://one.ubuntu.com/api/account/
539+ (see desktopcouch.replication_services.ubuntuone, which does this).
540+
541+ """
542+ if access_token is None:
543+ info = get_oauth_data()
544+ (access_token, consumer) = get_prod_oauth_token(info)
545+ # Set the signature method. This should be HMAC unless you have a jolly
546+ # good reason for it to not be.
547+ if sig_meth == "PLAINTEXT":
548+ signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
549+ else:
550+ signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
551+ if show_tokens:
552+ print "Using OAuth data:"
553+ print "consumer: %s : %s\ntoken: %s" % (
554+ consumer.key, consumer.secret, access_token)
555+
556+ info_url = "https://one.ubuntu.com/api/account/"
557+ oauth_header = get_oauth_request_header(
558+ consumer, access_token, info_url, signature_method)
559+
560+ userid, couch_root = get_user_info(info_url, oauth_header)
561+ schema, netloc, path, params, query, fragment = urlparse.urlparse(
562+ couch_root)
563+ if server_override:
564+ netloc = server_override
565+ # Don't escape the first /
566+ path = "/" + urllib.quote(path[1:], safe="")
567+ couch_root = urlparse.urlunparse((
568+ schema, netloc, path, params, query, fragment))
569+
570+ # Now use COUCH_ROOT and the specified user urlpath to get data
571+ if urlpath == "_all_dbs":
572+ couch_url = "%s%s" % (couch_root, urlpath)
573+ couch_url = urlparse.urlunparse((
574+ schema, netloc, "_all_dbs", None, "user_id=%s" % userid, None))
575+ else:
576+ couch_url = "%s%%2F%s" % (couch_root, urlpath)
577+ schema, netloc, path, params, query, fragment = urlparse.urlparse(
578+ couch_url)
579+ querystr_as_dict = dict(cgi.parse_qsl(query))
580+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(
581+ http_url=couch_url,
582+ http_method=http_method,
583+ oauth_consumer=consumer,
584+ token=access_token,
585+ parameters=querystr_as_dict)
586+ oauth_request.sign_request(signature_method, consumer, access_token)
587+ failed = 0
588+ while True:
589+ try:
590+ http = httplib2.Http()
591+ print couch_url, http_method
592+ resp, content = http.request(couch_url, http_method,
593+ headers=oauth_request.to_header(), body=request_body)
594+ break
595+ except IOError:
596+ sleep(2 ** failed) # 1, 2, 4, 8, 16
597+ failed += 1
598+ if failed > 5:
599+ break
600+ if failed:
601+ print "(request failed %s time%s)" % (
602+ failed, "s" if failed > 1 else "")
603+ if resp['status'] in ("200", "201"):
604+ return json.loads(content)
605+ elif resp['status'] == "400":
606+ print "The server could not parse the oauth token:\n%s" % content
607+ elif resp['status'] == "401":
608+ print "Access Denied"
609+ print "Content:"
610+ return content
611+ else:
612+ return (
613+ "There was a problem processing the request:\nstatus:%s, response:"
614+ " %r" % (resp['status'], content))

Subscribers

People subscribed via source and target branches

to all changes: