22
22
"""The Desktop Couch Records API."""
24
24
from couchdb import Server
25
from couchdb.client import ResourceNotFound, ResourceConflict
25
from couchdb.client import ResourceNotFound, ResourceConflict, Resource
26
26
from couchdb.design import ViewDefinition
28
27
from record import Record
29
from oauth import oauth
31
33
#DEFAULT_DESIGN_DOCUMENT = "design"
32
34
DEFAULT_DESIGN_DOCUMENT = None # each view in its own eponymous design doc.
43
45
return ("Database %s does not exist on this server. (Create it by "
44
46
"passing create=True)") % self.database
48
class OAuthAuthentication(httplib2.Authentication):
49
"""An httplib2.Authentication subclass for OAuth"""
50
def __init__(self, oauth_data, host, request_uri, headers, response,
52
self.oauth_data = oauth_data
53
httplib2.Authentication.__init__(self, None, host, request_uri,
54
headers, response, content, http)
56
def request(self, method, request_uri, headers, content):
57
"""Modify the request headers to add the appropriate
58
Authorization header."""
59
consumer = oauth.OAuthConsumer(self.oauth_data['consumer_key'],
60
self.oauth_data['consumer_secret'])
61
access_token = oauth.OAuthToken(self.oauth_data['token'],
62
self.oauth_data['token_secret'])
63
full_http_url = "http://%s%s" % (self.host, request_uri)
64
schema, netloc, path, params, query, fragment = urlparse.urlparse(full_http_url)
65
querystr_as_dict = dict(cgi.parse_qsl(query))
66
req = oauth.OAuthRequest.from_consumer_and_token(
70
http_url = full_http_url,
71
parameters = querystr_as_dict
73
req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, access_token)
74
headers.update(httplib2._normalize_headers(req.to_header()))
76
class OAuthCapableHttp(httplib2.Http):
77
"""Subclass of httplib2.Http which specifically uses our OAuth
78
Authentication subclass (because httplib2 doesn't know about it)"""
79
def add_oauth_tokens(self, consumer_key, consumer_secret,
82
"consumer_key": consumer_key,
83
"consumer_secret": consumer_secret,
85
"token_secret": token_secret
88
def _auth_from_challenge(self, host, request_uri, headers, response, content):
89
"""Since we know we're talking to desktopcouch, and we know that it
90
requires OAuth, just return the OAuthAuthentication here rather
91
than checking to see which supported auth method is required."""
92
yield OAuthAuthentication(self.oauth_data, host, request_uri, headers,
93
response, content, self)
47
95
def row_is_deleted(row):
48
96
"""Test if a row is marked as deleted. Smart views 'maps' should not
58
class CouchDatabase(object):
106
class CouchDatabaseBase(object):
59
107
"""An small records specific abstraction over a couch db database."""
61
def __init__(self, database, uri=None, record_factory=None, create=False,
109
def __init__(self, database, uri, record_factory=None, create=False,
62
110
server_class=Server):
64
desktopcouch.find_pid()
65
port = desktopcouch.find_port()
66
self.server_uri = "http://localhost:%s" % port
111
self.server_uri = uri
69
112
self._server = server_class(self.server_uri)
70
113
if database not in self._server:
226
269
def get_records(self, record_type=None, create_view=False,
227
design_doc=DEFAULT_DESIGN_DOCUMENT, version="1"):
270
design_doc=DEFAULT_DESIGN_DOCUMENT):
228
271
"""A convenience function to get records from a view named
229
C{get_records_and_type}, suffixed with C{__v} and the supplied version
230
string (or default of "1"). We optionally create a view in the design
272
C{get_records_and_type}. We optionally create a view in the design
231
273
document. C{create_view} may be True or False, and a special value,
232
274
None, is analogous to O_EXCL|O_CREAT .