Commit 7228a08b by Raphael Lullis

Adding support to ParseUser handling, account creation, login, and querying…

Adding support to ParseUser handling, account creation, login, and querying (through ParseUserQuery class)
parent e33d1674
...@@ -20,46 +20,133 @@ import collections ...@@ -20,46 +20,133 @@ import collections
import re import re
import logging import logging
API_ROOT = 'https://api.parse.com/1/classes' API_ROOT = 'https://api.parse.com/1'
APPLICATION_ID = '' APPLICATION_ID = ''
API_KEY = '' API_KEY = ''
MASTER_KEY = ''
log = logging.getLogger(__name__)
class ParseBinaryDataWrapper(str): class ParseBinaryDataWrapper(str):
pass pass
class ParseBase(object): class ParseBase(object):
def _executeCall(self, uri, http_verb, data=None): ENDPOINT_ROOT = API_ROOT
url = API_ROOT + uri
request = urllib2.Request(url, data) @classmethod
def execute(cls, uri, http_verb, extra_headers=None, **kw):
headers = extra_headers or {}
url = uri if uri.startswith(API_ROOT) else cls.ENDPOINT_ROOT + uri
data = kw and json.dumps(kw) or None
if http_verb == 'GET' and data:
url += '?%s' % urllib.urlencode(kw)
data = None
request = urllib2.Request(url, data)
request.add_header('Content-type', 'application/json') request.add_header('Content-type', 'application/json')
request.add_header('X-Parse-Application-Id', APPLICATION_ID) request.add_header('X-Parse-Application-Id', APPLICATION_ID)
request.add_header('X-Parse-REST-API-Key', API_KEY) request.add_header('X-Parse-REST-API-Key', API_KEY)
for header, value in headers.items(): request.add_header(header, value)
request.get_method = lambda: http_verb request.get_method = lambda: http_verb
# TODO: add error handling for server response # TODO: add error handling for server response
try: response = urllib2.urlopen(request)
response = urllib2.urlopen(request)
except urllib2.HTTPError, why:
#log.error(why)
return None
return json.loads(response.read()) return json.loads(response.read())
@classmethod
def GET(cls, uri, **kw):
return cls.execute(uri, 'GET', **kw)
@classmethod
def POST(cls, uri, **kw):
return cls.execute(uri, 'POST', **kw)
@classmethod
def PUT(cls, uri, **kw):
return cls.execute(uri, 'PUT', **kw)
@property
def _attributes(self):
# return "public" attributes converted to the base parse representation.
return dict([
self._convertToParseType(p) for p in self.__dict__.items()
if p[0][0] != '_'
])
def _isGeoPoint(self, value):
if isinstance(value, str):
return re.search('\\bPOINT\\(([-+]?[0-9]*\\.?[0-9]*) ' +
'([-+]?[0-9]*\\.?[0-9]*)\\)', value, re.I)
def _ISO8601ToDatetime(self, date_string): def _ISO8601ToDatetime(self, date_string):
# TODO: verify correct handling of timezone # TODO: verify correct handling of timezone
date_string = date_string[:-1] + 'UTC' date_string = date_string[:-1] + 'UTC'
return datetime.datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S.%f%Z') return datetime.datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S.%f%Z')
def _convertToParseType(self, prop):
key, value = prop
if type(value) == ParseObject:
value = {'__type': 'Pointer',
'className': value._class_name,
'objectId': value._object_id}
elif type(value) == datetime.datetime:
# 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)}
elif self._isGeoPoint(value):
coordinates = re.findall('[-+]?[0-9]+\\.?[0-9]*', value)
value = {'__type': 'GeoPoint',
'longitude': float(coordinates[0]),
'latitude': float(coordinates[1])}
return (key, value)
class ParseUser(ParseBase):
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)
@classmethod
def retrieve(cls, user_id):
return cls(**cls.GET('/' + user_id))
def __init__(self, **kw):
self._object_id = kw.pop('objectId', None)
self._updated_at = kw.pop('updatedAt', None)
self._created_at = kw.pop('createdAt', None)
for attr, value in kw.items():
self.__dict__[attr] = value
_absolute_url = property(lambda self: '/'.join([self.__class__.ENDPOINT_ROOT + self._object_id]))
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 ParseObject(ParseBase): class ParseObject(ParseBase):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'classes'])
def __init__(self, class_name, attrs_dict=None): def __init__(self, class_name, attrs_dict=None):
self._class_name = class_name self._class_name = class_name
self._object_id = None self._object_id = None
...@@ -92,7 +179,7 @@ class ParseObject(ParseBase): ...@@ -92,7 +179,7 @@ class ParseObject(ParseBase):
uri = '/%s/%s' % (self._class_name, self._object_id) uri = '/%s/%s' % (self._class_name, self._object_id)
self._executeCall(uri, 'DELETE') self.__class__.execute(uri, 'DELETE')
self = self.__init__(None) self = self.__init__(None)
...@@ -102,9 +189,13 @@ class ParseObject(ParseBase): ...@@ -102,9 +189,13 @@ class ParseObject(ParseBase):
it does not wait for save() to be called it does not wait for save() to be called
""" """
uri = '/%s/%s' % (self._class_name, self._object_id) uri = '/%s/%s' % (self._class_name, self._object_id)
txdata = '{"%s": {"__op": "Increment", "amount": %d}}' % (key, amount) payload = {
ret = self._executeCall(uri, 'PUT', txdata) key: {
self._populateFromDict(ret) '__op': 'Increment',
'amount': amount
}
}
self._populateFromDict(self.__class__.execute(uri, 'PUT', **payload))
def has(self, attr): def has(self, attr):
return attr in self.__dict__ return attr in self.__dict__
...@@ -114,14 +205,9 @@ class ParseObject(ParseBase): ...@@ -114,14 +205,9 @@ class ParseObject(ParseBase):
def refresh(self): def refresh(self):
uri = '/%s/%s' % (self._class_name, self._object_id) uri = '/%s/%s' % (self._class_name, self._object_id)
response_dict = self._executeCall(uri, 'GET') response_dict = self.__class__.execute(uri, 'GET')
self._populateFromDict(response_dict) self._populateFromDict(response_dict)
def _isGeoPoint(self, value):
if isinstance(value, str):
return re.search('\\bPOINT\\(([-+]?[0-9]*\\.?[0-9]*) ' +
'([-+]?[0-9]*\\.?[0-9]*)\\)', value, re.I)
def _populateFromDict(self, attrs_dict): def _populateFromDict(self, attrs_dict):
if 'objectId' in attrs_dict: if 'objectId' in attrs_dict:
self._object_id = attrs_dict['objectId'] self._object_id = attrs_dict['objectId']
...@@ -137,27 +223,6 @@ class ParseObject(ParseBase): ...@@ -137,27 +223,6 @@ class ParseObject(ParseBase):
self.__dict__.update(attrs_dict) self.__dict__.update(attrs_dict)
def _convertToParseType(self, prop):
key, value = prop
if type(value) == ParseObject:
value = {'__type': 'Pointer',
'className': value._class_name,
'objectId': value._object_id}
elif type(value) == datetime.datetime:
# 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)}
elif self._isGeoPoint(value):
coordinates = re.findall('[-+]?[0-9]+\\.?[0-9]*', value)
value = {'__type': 'GeoPoint',
'longitude': float(coordinates[0]),
'latitude': float(coordinates[1])}
return (key, value)
def _convertFromParseType(self, prop): def _convertFromParseType(self, prop):
key, value = prop key, value = prop
...@@ -176,12 +241,13 @@ class ParseObject(ParseBase): ...@@ -176,12 +241,13 @@ class ParseObject(ParseBase):
return (key, value) return (key, value)
def _getJSONProperties(self): @property
# filter properties that start with an underscore, and convert them. def _attributes(self):
return json.dumps(dict([ # return "public" attributes converted to the base parse representation.
self._convertToParseType(p) for p in self.__dict__.items() return dict([
if p[0][0] != '_' self._convertToParseType(p) for p in self.__dict__.items()
])) if p[0][0] != '_'
])
def _create(self): def _create(self):
# URL: /1/classes/<className> # URL: /1/classes/<className>
...@@ -189,9 +255,7 @@ class ParseObject(ParseBase): ...@@ -189,9 +255,7 @@ class ParseObject(ParseBase):
uri = '/%s' % self._class_name uri = '/%s' % self._class_name
data = self._getJSONProperties() response_dict = self.__class__.POST(uri, **self._attributes)
response_dict = self._executeCall(uri, 'POST', data)
self._created_at = self._updated_at = response_dict['createdAt'] self._created_at = self._updated_at = response_dict['createdAt']
self._object_id = response_dict['objectId'] self._object_id = response_dict['objectId']
...@@ -202,14 +266,14 @@ class ParseObject(ParseBase): ...@@ -202,14 +266,14 @@ class ParseObject(ParseBase):
uri = '/%s/%s' % (self._class_name, self._object_id) uri = '/%s/%s' % (self._class_name, self._object_id)
data = self._getJSONProperties() response_dict = self.execute(uri, 'PUT', **self._attributes)
response_dict = self._executeCall(uri, 'PUT', data)
self._updated_at = response_dict['updatedAt'] self._updated_at = response_dict['updatedAt']
class ParseQuery(ParseBase): class ParseQuery(ParseBase):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'classes'])
def __init__(self, class_name): def __init__(self, class_name):
self._class_name = class_name self._class_name = class_name
self._where = collections.defaultdict(dict) self._where = collections.defaultdict(dict)
...@@ -269,7 +333,7 @@ class ParseQuery(ParseBase): ...@@ -269,7 +333,7 @@ class ParseQuery(ParseBase):
# HTTP Verb: GET # HTTP Verb: GET
if self._object_id: if self._object_id:
uri = '/%s/%s' % (self._class_name, self._object_id) response = self.__class__.GET('/%s/%s' % (self._class_name, self._object_id))
else: else:
options = dict(self._options) # make a local copy options = dict(self._options) # make a local copy
if self._where: if self._where:
...@@ -277,12 +341,29 @@ class ParseQuery(ParseBase): ...@@ -277,12 +341,29 @@ class ParseQuery(ParseBase):
where = json.dumps(self._where) where = json.dumps(self._where)
options.update({'where': where}) options.update({'where': where})
uri = '/%s?%s' % (self._class_name, urllib.urlencode(options)) response = self.__class__.GET('/%s' % self._class_name, **options)
response_dict = self._executeCall(uri, 'GET')
if single_result: if single_result:
return ParseObject(self._class_name, response_dict) return ParseObject(self._class_name, response)
else: else:
return [ParseObject(self._class_name, result) return [ParseObject(self._class_name, result) for result in response['results']]
for result in response_dict['results']]
class ParseUserQuery(ParseQuery):
ENDPOINT_ROOT = '/'.join([API_ROOT, 'users'])
def __init__(self):
self._where = collections.defaultdict(dict)
self._options = {}
def get(self, object_id):
return ParseUser.retrieve(object_id)
def _fetch(self):
options = dict(self._options) # make a local copy
if self._where:
# JSON encode WHERE values
where = json.dumps(self._where)
options.update({'where': where})
response = self.__class__.GET('', **options)
return [ParseUser(**result) for result in response['results']]
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