diff --git a/setup.py b/setup.py index b4ff2f7..a83ef7b 100644 --- a/setup.py +++ b/setup.py @@ -30,4 +30,4 @@ install_requires=['httplib2>=0.6.0', 'oauth2>=1.1.3', 'simplejson>=2.0.9'], keywords="simplegeo", zip_safe = True, - tests_require=['nose', 'coverage', 'python-geohash>=0.2']) + tests_require=['nose', 'coverage', 'python-geohash>=0.2', 'mock']) diff --git a/simplegeo/__init__.py b/simplegeo/__init__.py index def129b..4b5b6ac 100644 --- a/simplegeo/__init__.py +++ b/simplegeo/__init__.py @@ -66,6 +66,47 @@ def __hash__(self): def __eq__(self, other): return isinstance(other, self.__class__) and self.id == other.id +class Layer(object): + def __init__(self, name, title='', description='', public=False, + callback_urls=[]): + self.name = name + self.title = title + self.description = description + self.public = public + self.callback_urls = callback_urls + + @classmethod + def from_dict(cls, data): + if not data: + return None + layer = cls(data.get('name'), data.get('title'), + data.get('description'), data.get('public'), + data.get('callback_urls', [])) + return layer + + def to_dict(self): + return { + 'name': self.name, + 'title': self.title, + 'description': self.description, + 'public': self.public, + 'callback_urls': self.callback_urls, + } + + def to_json(self): + return json.dumps(self.to_dict()) + + def __str__(self): + return self.to_json() + + def __repr__(self): + return "Layer(name=%s, public=%s)" % (self.name, self.public) + + def __hash__(self): + return hash((self.name)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.name == other.name class APIError(Exception): """Base exception for all API errors.""" @@ -115,6 +156,8 @@ class Client(object): 'density_day': 'density/%(day)s/%(lat)s,%(lon)s.json', 'density_hour': 'density/%(day)s/%(hour)s/%(lat)s,%(lon)s.json', 'layer': 'layer/%(layer)s.json', + 'new_layer': 'layers/%(layer)s.json', + 'layers': 'layers.json', 'contains' : 'contains/%(lat)s,%(lon)s.json', 'overlaps' : 'overlaps/%(south)s,%(west)s,%(north)s,%(east)s.json', 'boundary' : 'boundary/%(id)s.json', @@ -155,7 +198,7 @@ def add_record(self, record): raise Exception("Record has no layer.") endpoint = self.endpoint('record', layer=record.layer, id=record.id) - self._request(endpoint, "PUT", record.to_json()) + return self._request(endpoint, "PUT", record.to_json()) def add_records(self, layer, records): features = { @@ -163,11 +206,11 @@ def add_records(self, layer, records): 'features': [record.to_dict() for record in records], } endpoint = self.endpoint('add_records', layer=layer) - self._request(endpoint, "POST", json.dumps(features)) + return self._request(endpoint, "POST", json.dumps(features)) def delete_record(self, layer, id): endpoint = self.endpoint('record', layer=layer, id=id) - self._request(endpoint, "DELETE") + return self._request(endpoint, "DELETE") def get_record(self, layer, id): endpoint = self.endpoint('record', layer=layer, id=id) @@ -202,6 +245,14 @@ def get_layer(self, layer): endpoint = self.endpoint('layer', layer=layer) return self._request(endpoint, "GET") + def get_layers(self): + endpoint = self.endpoint('layers') + return self._request(endpoint, "GET") + + def create_layer(self, layer): + endpoint = self.endpoint('new_layer', layer=layer.name) + return self._request(endpoint, "PUT", layer.to_json()) + def get_density(self, lat, lon, day, hour=None): if hour is not None: endpoint = self.endpoint('density_hour', lat=lat, lon=lon, day=day, hour=hour) diff --git a/simplegeo/twisted/client.py b/simplegeo/twisted/client.py index 4ee5c7a..96b4321 100644 --- a/simplegeo/twisted/client.py +++ b/simplegeo/twisted/client.py @@ -57,8 +57,10 @@ def _request(self, endpoint, method, data=None): code = str(response.code) message = body if isinstance(body, dict): - code = body['code'] - message = body['message'] + if 'code' in body: + code = body['code'] + if 'message' in body: + message = body['message'] raise simplegeo.APIError(code, message, response.headers) diff --git a/tests/twisted/test_client.py b/tests/twisted/test_client.py new file mode 100644 index 0000000..64e44bc --- /dev/null +++ b/tests/twisted/test_client.py @@ -0,0 +1,55 @@ +import mock +from twisted.trial import unittest +from twisted.internet import defer, reactor +from simplegeo import Record, APIError +from simplegeo.twisted import Client + +MY_OAUTH_KEY = 'MY_OAUTH_KEY' +MY_OAUTH_SECRET = 'MY_SECRET_KEY' +TESTING_LAYER = 'TESTING_LAYER' + +API_VERSION = '0.1' +API_HOST = 'api.simplegeo.com' +API_PORT = 80 + +class ClientTest(unittest.TestCase): + + def setUp(self): + self.client = Client(MY_OAUTH_KEY, MY_OAUTH_SECRET, API_VERSION, API_HOST, API_PORT) + + def fake_request(self, *args, **kwargs): + d = defer.Deferred() + reactor.callLater(0, d.callback, mock.sentinel.retval) + return d + + @defer.inlineCallbacks + def test_add_record_returns_deferred(self): + self.client._request = mock.Mock(wraps=self.fake_request) + record = Record(layer=TESTING_LAYER, id=69, lat=34.5, lon=-122.8) + deferred = self.client.add_record(record) + self.assertTrue(isinstance(deferred, defer.Deferred)) + retval = yield deferred + self.assertEquals(mock.sentinel.retval, retval) + + @defer.inlineCallbacks + def test_add_records_returns_deferred(self): + self.client._request = mock.Mock(wraps=self.fake_request) + records = [Record(layer=TESTING_LAYER, id=69, lat=34.5, lon=-122.8), Record(layer=TESTING_LAYER, id=70, lat=34.5, lon=-122.8)] + deferred = self.client.add_records(TESTING_LAYER, records) + self.assertTrue(isinstance(deferred, defer.Deferred)) + retval = yield deferred + self.assertEquals(mock.sentinel.retval, retval) + + @defer.inlineCallbacks + def test_delete_record_returns_deferred(self): + self.client._request = mock.Mock(wraps=self.fake_request) + deferred = self.client.delete_record(TESTING_LAYER, 4) + self.assertTrue(isinstance(deferred, defer.Deferred)) + retval = yield deferred + self.assertEquals(mock.sentinel.retval, retval) + +if __name__ == '__main__': + import sys + from twisted.scripts import trial + sys.argv.extend([sys.argv[0]]) + trial.run()