Commit 733651fd by David Robinson

Redesigned User class so that it could work as an object (adding and saving…

Redesigned User class so that it could work as an object (adding and saving attributes, added delete method, and lets it save a session token). Required changing UserQuery as well. Added a few appropriate test cases in TestUser class. Finally, added ParseError exception class. Addresses issue in #5.
parent c715cc36
...@@ -42,7 +42,7 @@ class ParseBase(object): ...@@ -42,7 +42,7 @@ class ParseBase(object):
url += '?%s' % urllib.urlencode(kw) url += '?%s' % urllib.urlencode(kw)
data = None data = None
request = urllib2.Request(url, data) request = urllib2.Request(url, data, headers)
request.add_header('Content-type', 'application/json') request.add_header('Content-type', 'application/json')
#auth_header = "Basic %s" % base64.b64encode('%s:%s' % #auth_header = "Basic %s" % base64.b64encode('%s:%s' %
# (APPLICATION_ID, REST_API_KEY)) # (APPLICATION_ID, REST_API_KEY))
...@@ -151,32 +151,6 @@ class ParseResource(ParseBase): ...@@ -151,32 +151,6 @@ class ParseResource(ParseBase):
or None) or None)
class User(ParseResource):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'users'])
@classmethod
def signup(cls, username, password, **kw):
return cls.POST('', username=username, password=password, **kw)
@classmethod
def login(cls, username, password):
return cls.GET('/'.join([API_ROOT, 'login']), username=username,
password=password)
@classmethod
def request_password_reset(cls, email):
return cls.POST('/'.join([API_ROOT, 'requestPasswordReset']),
email=email)
def save(self, session=None):
session_header = {'X-Parse-Session-Token': session and
session.get('sessionToken')}
return self.__class__.PUT(
self._absolute_url, extra_headers=session_header,
**self._attributes)
class Installation(ParseResource): class Installation(ParseResource):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'installations']) ENDPOINT_ROOT = '/'.join([API_ROOT, 'installations'])
...@@ -286,6 +260,80 @@ class Object(ParseResource): ...@@ -286,6 +260,80 @@ class Object(ParseResource):
self._updated_at = response_dict['updatedAt'] self._updated_at = response_dict['updatedAt']
class User(Object):
"""
A User is like a regular Parse object (can be modified and saved) but
it requires additional methods and functionality
"""
ENDPOINT_ROOT = '/'.join([API_ROOT, 'users'])
def __init__(self, username, password=None, **kw):
"""
Initialized with a username and possibly password along with any other
attributes (name, phone number...)
"""
kw["username"] = username
kw["password"] = password
Object.__init__(self, "", kw)
@classmethod
def retrieve(cls, resource_id):
"""retrieve a user by its ID"""
ret = cls.GET('/' + resource_id)
username = ret.pop("username")
return cls(username, **ret)
def needs_session(func):
"""decorator describing User methods that need to be logged in"""
def ret(obj, *a, **kw):
if not hasattr(obj, "sessionToken"):
raise ParseError("%s requires a logged-in session" %
(func.__name__, ))
func(obj, *a, **kw)
return ret
def signup(self, **kw):
"""same as creating an object, with handling if user already exists"""
try:
self._create()
except urllib2.HTTPError as e:
if "400" in str(e):
raise ParseError("User already exists")
else:
raise
def login(self):
try:
ret = self.GET('/'.join([API_ROOT, 'login']),
username=self.username, password=self.password)
except urllib2.HTTPError:
raise ParseError("Invalid login")
# update all attributes
self._populateFromDict(ret)
@needs_session
def save(self):
session_header = {'X-Parse-Session-Token': self.sessionToken}
# remove items you can't change
save_dict = {k: v for k, v in self._attributes.items()
if k not in ["username", "password", "sessionToken"]}
return self.__class__.PUT(
self._absolute_url, extra_headers=session_header,
**save_dict)
@needs_session
def delete(self):
session_header = {'X-Parse-Session-Token': self.sessionToken}
return self.DELETE("/" + self.objectId(), extra_headers=session_header)
@classmethod
def request_password_reset(cls, email):
"""reset a user's password using his email"""
return self.POST('/'.join([API_ROOT, 'requestPasswordReset']),
email=email)
class Push(ParseResource): class Push(ParseResource):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'push']) ENDPOINT_ROOT = '/'.join([API_ROOT, 'push'])
...@@ -405,6 +453,10 @@ class UserQuery(Query): ...@@ -405,6 +453,10 @@ class UserQuery(Query):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'users']) ENDPOINT_ROOT = '/'.join([API_ROOT, 'users'])
QUERY_CLASS = User QUERY_CLASS = User
def __init__(self):
"""UserQuery doesn't need a class name to be passed"""
pass
class InstallationQuery(Query): class InstallationQuery(Query):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'installations']) ENDPOINT_ROOT = '/'.join([API_ROOT, 'installations'])
...@@ -422,3 +474,10 @@ class InstallationQuery(Query): ...@@ -422,3 +474,10 @@ class InstallationQuery(Query):
**options) **options)
return [self.__class__.QUERY_CLASS(**result) return [self.__class__.QUERY_CLASS(**result)
for result in response['results']] for result in response['results']]
class ParseError(Exception):
"""
Represents exceptions coming from Parse (e.g. invalid login or signup)
"""
pass
...@@ -205,6 +205,48 @@ class TestFunction(unittest.TestCase): ...@@ -205,6 +205,48 @@ class TestFunction(unittest.TestCase):
self.assertAlmostEqual(ret["result"], 4.5) self.assertAlmostEqual(ret["result"], 4.5)
class TestUser(unittest.TestCase):
def setUp(self):
"""remove the test user if he exists"""
u = parse_rest.User("dhelmet@spaceballs.com", "12345")
try:
u.login()
u.delete()
except parse_rest.ParseError as e:
# if the user doesn't exist, that's fine
if e.message != "Invalid login":
raise
def test_user(self):
"""Test the ability to sign up, log in, and delete users"""
u = parse_rest.User("dhelmet@spaceballs.com", "12345")
u.signup()
# can't save or delete until it's logged in
self.assertRaises(parse_rest.ParseError, u.save, ())
self.assertRaises(parse_rest.ParseError, u.delete, ())
u.login()
self.assertTrue(hasattr(u, "sessionToken"))
self.assertNotEqual(u.sessionToken, None)
# add phone number and save
u.phone = "555-5555"
u.save()
uq = parse_rest.UserQuery()
u_retrieved = uq.get(u.objectId())
self.assertEqual(u.username, u_retrieved.username)
self.assertEqual(u_retrieved.phone, "555-5555")
# try creating another account with the same user
u2 = parse_rest.User("dhelmet@spaceballs.com", "12345")
self.assertRaises(parse_rest.ParseError, u2.signup)
# time to delete
u.delete()
if __name__ == "__main__": if __name__ == "__main__":
# command line # command line
unittest.main() unittest.main()
...@@ -22,7 +22,7 @@ class TestCommand(Command): ...@@ -22,7 +22,7 @@ class TestCommand(Command):
setup( setup(
name='parse_rest', name='parse_rest',
version='0.3.2012', version='0.4.2012',
description='A client library for Parse.com\'.s REST API', description='A client library for Parse.com\'.s REST API',
url='https://github.com/dgrtwo/ParsePy', url='https://github.com/dgrtwo/ParsePy',
packages=['parse_rest'], packages=['parse_rest'],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment