blob: 8525e8dade308aba371dcfd67f127010e99f4721 [file] [log] [blame]
showardf828c772010-01-25 21:49:42 +00001import logging, pprint, re, urllib
2import httplib2
3from django.utils import simplejson
4
5
showardf828c772010-01-25 21:49:42 +00006_http = httplib2.Http()
7
8
9class RestClientError(Exception):
10 pass
11
12
13class ClientError(Exception):
14 pass
15
16
17class ServerError(Exception):
18 pass
19
20
21class Response(object):
22 def __init__(self, httplib_response, httplib_content):
23 self.status = int(httplib_response['status'])
24 self.headers = httplib_response
25 self.entity_body = httplib_content
26
27
28 def decoded_body(self):
29 return simplejson.loads(self.entity_body)
30
31
32 def __str__(self):
33 return '\n'.join([str(self.status), self.entity_body])
34
35
36class Resource(object):
showardf46ad4c2010-02-03 20:28:59 +000037 def __init__(self, representation_dict):
showardf828c772010-01-25 21:49:42 +000038 assert 'href' in representation_dict
39 for key, value in representation_dict.iteritems():
40 setattr(self, str(key), value)
41
42
43 def __repr__(self):
44 return 'Resource(%r)' % self._representation()
45
46
47 def pprint(self):
48 # pretty-print support for debugging/interactive use
49 pprint.pprint(self._representation())
50
51
52 @classmethod
showardf46ad4c2010-02-03 20:28:59 +000053 def load(cls, uri):
54 directory = cls({'href': uri})
showardf828c772010-01-25 21:49:42 +000055 return directory.get()
56
57
58 def _read_representation(self, value):
59 # recursively convert representation dicts to Resource objects
60 if isinstance(value, list):
61 return [self._read_representation(element) for element in value]
62 if isinstance(value, dict):
63 converted_dict = dict((key, self._read_representation(sub_value))
64 for key, sub_value in value.iteritems())
65 if 'href' in converted_dict:
showardf46ad4c2010-02-03 20:28:59 +000066 return type(self)(converted_dict)
showardf828c772010-01-25 21:49:42 +000067 return converted_dict
68 return value
69
70
71 def _write_representation(self, value):
72 # recursively convert Resource objects to representation dicts
73 if isinstance(value, list):
74 return [self._write_representation(element) for element in value]
75 if isinstance(value, dict):
76 return dict((key, self._write_representation(sub_value))
77 for key, sub_value in value.iteritems())
78 if isinstance(value, Resource):
79 return value._representation()
80 return value
81
82
83 def _representation(self):
84 return dict((key, self._write_representation(value))
85 for key, value in self.__dict__.iteritems()
86 if not key.startswith('_')
87 and not callable(value))
88
89
showardf46ad4c2010-02-03 20:28:59 +000090 @classmethod
91 def _do_request(self, method, uri, query_parameters, encoded_body):
showardf828c772010-01-25 21:49:42 +000092 if query_parameters:
showardf46ad4c2010-02-03 20:28:59 +000093 query_string = '?' + urllib.urlencode(query_parameters)
94 else:
95 query_string = ''
96 full_uri = uri + query_string
showardf828c772010-01-25 21:49:42 +000097
98 if encoded_body:
99 entity_body = simplejson.dumps(encoded_body)
100 else:
101 entity_body = None
102
showardf828c772010-01-25 21:49:42 +0000103 logging.debug('%s %s', method, full_uri)
104 if entity_body:
105 logging.debug(entity_body)
106 headers, response_body = _http.request(
showardf46ad4c2010-02-03 20:28:59 +0000107 full_uri, method, body=entity_body,
showardf828c772010-01-25 21:49:42 +0000108 headers={'Content-Type': 'application/json'})
109 logging.debug('Response: %s', headers['status'])
110
showardf46ad4c2010-02-03 20:28:59 +0000111 return Response(headers, response_body)
112
113
114 def _request(self, method, query_parameters=None, encoded_body=None):
115 if query_parameters is None:
116 query_parameters = {}
117
118 response = self._do_request(method, self.href, query_parameters,
119 encoded_body)
120
showardf828c772010-01-25 21:49:42 +0000121 if 300 <= response.status < 400: # redirection
122 raise NotImplementedError(str(response)) # TODO
123 if 400 <= response.status < 500:
124 raise ClientError(str(response))
125 if 500 <= response.status < 600:
126 raise ServerError(str(response))
127 return response
128
129
130 def _stringify_query_parameter(self, value):
131 if isinstance(value, (list, tuple)):
132 return ','.join(value)
133 return str(value)
134
135
136 def get(self, **query_parameters):
137 string_parameters = dict((key, self._stringify_query_parameter(value))
138 for key, value in query_parameters.iteritems()
139 if value is not None)
140 response = self._request('GET', query_parameters=string_parameters)
141 assert response.status == 200
142 return self._read_representation(response.decoded_body())
143
144
145 def put(self):
146 response = self._request('PUT', encoded_body=self._representation())
147 assert response.status == 200
148 return self._read_representation(response.decoded_body())
149
150
151 def delete(self):
152 response = self._request('DELETE')
153 assert response.status == 204 # no content
154
155
156 def post(self, request_dict):
157 # request_dict may still have resources in it
158 request_dict = self._write_representation(request_dict)
159 response = self._request('POST', encoded_body=request_dict)
160 assert response.status == 201 # created
161 return self._read_representation({'href': response.headers['location']})