Commit 248b7569 by David Robinson

Auth fixes, tests.py, increment method, pep8

Fixed authentication, since Parse appears to have changed their API.

Added tests.py with unit tests for ParseObject and ParseQuery classes.

Added ParseObject.increment(key) method.

pep8 compliance changes, mostly removing end of line whitespace
parent f93309cf
......@@ -11,7 +11,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import urllib, urllib2
import urllib
import urllib2
import base64
import json
import datetime
......@@ -35,9 +36,11 @@ class ParseBase(object):
request.add_header('Content-type', 'application/json')
# we could use urllib2's authentication system, but it seems like overkill for this
auth_header = "Basic %s" % base64.b64encode('%s:%s' % (APPLICATION_ID, MASTER_KEY))
request.add_header("Authorization", auth_header)
#auth_header = "Basic %s" % base64.b64encode('%s:%s' %
# (APPLICATION_ID, MASTER_KEY))
#request.add_header("Authorization", auth_header)
request.add_header("X-Parse-Application-Id", APPLICATION_ID)
request.add_header("X-Parse-REST-API-Key", MASTER_KEY)
request.get_method = lambda: http_verb
......@@ -51,7 +54,8 @@ class ParseBase(object):
def _ISO8601ToDatetime(self, date_string):
# TODO: verify correct handling of timezone
date_string = date_string[:-1] + 'UTC'
date = datetime.datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%S.%f%Z")
date = datetime.datetime.strptime(date_string,
"%Y-%m-%dT%H:%M:%S.%f%Z")
return date
......@@ -69,10 +73,12 @@ class ParseObject(ParseBase):
return self._object_id
def updatedAt(self):
return self._updated_at and self._ISO8601ToDatetime(self._updated_at) or None
return (self._updated_at and self._ISO8601ToDatetime(self._updated_at)
or None)
def createdAt(self):
return self._created_at and self._ISO8601ToDatetime(self._created_at) or None
return (self._created_at and self._ISO8601ToDatetime(self._created_at)
or None)
def save(self):
if self._object_id:
......@@ -90,13 +96,36 @@ class ParseObject(ParseBase):
self = self.__init__(None)
def increment(self, key, amount=1):
"""
Increment one value in the object. Note that this happens immediately:
it does not wait for save() to be called
"""
uri = '/%s/%s' % (self._class_name, self._object_id)
txdata = '{"%s": {"__op": "Increment", "amount": %d}}' % (key, amount)
ret = self._executeCall(uri, 'PUT', txdata)
self._populateFromDict(ret)
def has(self, attr):
return attr in self.__dict__
def remove(self, attr):
self.__dict__.pop(attr)
def refresh(self):
uri = '/%s/%s' % (self._class_name, self._object_id)
response_dict = self._executeCall(uri, 'GET')
self._populateFromDict(response_dict)
def _populateFromDict(self, attrs_dict):
if 'objectId' in attrs_dict:
self._object_id = attrs_dict['objectId']
self._created_at = attrs_dict['createdAt']
self._updated_at = attrs_dict['updatedAt']
del attrs_dict['objectId']
if 'createdAt' in attrs_dict:
self._created_at = attrs_dict['createdAt']
del attrs_dict['createdAt']
if 'updatedAt' in attrs_dict:
self._updated_at = attrs_dict['updatedAt']
del attrs_dict['updatedAt']
attrs_dict = dict(map(self._convertFromParseType, attrs_dict.items()))
......@@ -111,8 +140,8 @@ class ParseObject(ParseBase):
'className': value._class_name,
'objectId': value._object_id}
elif type(value) == datetime.datetime:
value = {'__type': 'Date',
'iso': value.isoformat()[:-3] + 'Z'} # take off the last 3 digits and add a Z
# take off the last 3 digits and add a Z
value = {'__type': 'Date', 'iso': value.isoformat()[:-3] + 'Z'}
elif type(value) == ParseBinaryDataWrapper:
value = {'__type': 'Bytes',
'base64': base64.b64encode(value)}
......@@ -122,13 +151,14 @@ class ParseObject(ParseBase):
def _convertFromParseType(self, prop):
key, value = prop
if type(value) == dict and value.has_key('__type'):
if type(value) == dict and '__type' in value:
if value['__type'] == 'Pointer':
value = ParseQuery(value['className']).get(value['objectId'])
elif value['__type'] == 'Date':
value = self._ISO8601ToDatetime(value['iso'])
elif value['__type'] == 'Bytes':
value = ParseBinaryDataWrapper(base64.b64decode(value['base64']))
value = ParseBinaryDataWrapper(base64.b64decode(
value['base64']))
else:
raise Exception('Invalid __type.')
......@@ -139,9 +169,11 @@ class ParseObject(ParseBase):
properties_list = self.__dict__.items()
# filter properties that start with an underscore
properties_list = filter(lambda prop: prop[0][0] != '_', properties_list)
properties_list = filter(lambda prop: prop[0][0] != '_',
properties_list)
#properties_list = [(key, value) for key, value in self.__dict__.items() if key[0] != '_']
#properties_list = [(key, value) for key, value
# in self.__dict__.items() if key[0] != '_']
properties_list = map(self._convertToParseType, properties_list)
......@@ -227,8 +259,8 @@ class ParseQuery(ParseBase):
return self._fetch(single_result=True)
def fetch(self):
# hide the single_result param of the _fetch method from the library user
# since it's only useful internally
# hide the single_result param of the _fetch method from the library
# user since it's only useful internally
return self._fetch()
def _fetch(self, single_result=False):
......@@ -251,5 +283,5 @@ class ParseQuery(ParseBase):
if single_result:
return ParseObject(self._class_name, response_dict)
else:
return [ParseObject(self._class_name, result) for result in response_dict['results']]
return [ParseObject(self._class_name, result)
for result in response_dict['results']]
"""
Contains unit tests for the Python Parse REST API wrapper
"""
import unittest
import urllib2
import datetime
import __init__ as ParsePy
try:
import settings_local
except ImportError:
raise ImportError("You must create a settings_local.py file " +
"with an example application to run tests")
ParsePy.APPLICATION_ID = settings_local.APPLICATION_ID
ParsePy.MASTER_KEY = settings_local.MASTER_KEY
### FUNCTIONS ###
def test_obj(saved=False):
"""Return a test ParsePy.ParseObject (content is from the docs)"""
ret = ParsePy.ParseObject("GameScore")
ret.score = 1337
ret.playerName = "Sean Plott"
ret.cheatMode = False
if saved:
ret.save()
return ret
### CLASSES ###
class TestParseObjectAndQuery(unittest.TestCase):
"""
Tests for the ParsePy.ParseObject interface for creating and updating Parse
objects, as well as the ParsePy.ParseQuery interface for retrieving them
"""
def check_test_obj(self, o):
"""check that the object is consistent with the test object"""
self.assertEqual(o.objectId().__class__, unicode)
self.assertEqual(o.updatedAt().__class__, datetime.datetime)
self.assertEqual(o.createdAt().__class__, datetime.datetime)
self.assertEqual(o.score, 1337)
# TODO: str vs unicode
#self.assertEqual(o.playerName.__class__, unicode)
self.assertEqual(o.cheatMode.__class__, bool)
def test_object(self):
"""Test the creation, retrieval and updating of a ParseObject"""
gameScore = test_obj()
gameScore.save()
self.check_test_obj(gameScore)
# retrieve a new one
query = ParsePy.ParseQuery("GameScore")
obj1 = query.get(gameScore.objectId())
self.check_test_obj(obj1)
# now update it
current_updated = obj1.updatedAt()
obj1.score = 1000
obj1.save()
self.assertGreater(obj1.updatedAt(), current_updated)
self.assertEqual(obj1.score, 1000)
# re-retrieve it
obj2 = query.get(obj1.objectId())
self.assertEqual(obj2.score, 1000)
# change one object, check that others can be refreshed
obj2.score = 2000
obj2.save()
self.assertEqual(obj1.score, 1000)
obj1.refresh()
self.assertEqual(obj1.score, 2000)
# try removing a field
obj2.remove("score")
obj2.save()
self.assertEqual(obj2.has("score"), False)
def test_increment(self):
"""Test incrementation of fields"""
o = test_obj(True)
self.check_test_obj(o)
o.save()
o.increment("score")
self.assertEqual(o.score, 1338)
query = ParsePy.ParseQuery("GameScore")
o2 = query.get(o.objectId())
self.assertEqual(o2.score, 1338)
# one more time
o.increment("score")
self.assertEqual(o.score, 1339)
o3 = query.get(o.objectId())
self.assertEqual(o3.score, 1339)
def test_relationship(self):
"""Test relationship between objects"""
post = ParsePy.ParseObject("Post")
post.title = "I'm Hungry"
post.content = "Where should we go for lunch?"
post.save()
comment = ParsePy.ParseObject("Comment")
comment.content = "Let's do Sushirrito"
comment.parent = post
comment.save()
# that should have saved both post and comment
post_id = post.objectId()
comment_id = comment.objectId()
self.assertEqual(post_id.__class__, unicode)
self.assertEqual(comment_id.__class__, unicode)
# retrieve new ones
post2 = ParsePy.ParseQuery("Post").get(post_id)
comment2 = ParsePy.ParseQuery("Comment").get(comment_id)
# check the relationship between the saved post and comment
self.assertEqual(comment2.parent.objectId(), post_id)
self.assertEqual(comment2.parent.title, "I'm Hungry")
if __name__ == "__main__":
# command line
unittest.main()
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