Commit b640110e by David Robinson

Added Function class to handle calling CloudCode functions. Also added test…

Added Function class to handle calling CloudCode functions. Also added test cases for hello and averageStars functions, which necessitated adding a cloudcode directory to upload those functions to the test application, as well as requiring MASTER_KEY in the settings_local.py file. Added CloudCode documentation to README.mkd.
parent 895cb688
...@@ -5,4 +5,5 @@ ...@@ -5,4 +5,5 @@
build/* build/*
settings_local.py settings_local.py
dist dist
MANIFEST MANIFEST
\ No newline at end of file global.json
...@@ -27,6 +27,21 @@ and performing the commands: ...@@ -27,6 +27,21 @@ and performing the commands:
(again you may have to add `sudo` before `python setup.py install`). (again you may have to add `sudo` before `python setup.py install`).
Testing
-------
To run the tests, you need to:
* create a `settings_local.py` file in your local directory with three variables that define a sample Parse application to use for testing:
~~~~~ {python}
APPLICATION_ID = "APPLICATION_ID_HERE"
REST_API_KEY = "REST_API_KEY_HERE"
MASTER_KEY = "MASTER_KEY_HERE"
~~~~~
* install the [Parse CloudCode command line tool](https://www.parse.com/docs/cloud_code_guide)
You can then test the installation by running: You can then test the installation by running:
python setup.py test python setup.py test
...@@ -164,4 +179,47 @@ We can also order the results using: ...@@ -164,4 +179,47 @@ We can also order the results using:
* **Order** * **Order**
* order(_parameter_name_, _decending_=False) * order(_parameter_name_, _decending_=False)
Cloud Functions
---------------
Parse offers [CloudCode](https://www.parse.com/docs/cloud_code_guide), which has the ability to upload JavaScript functions that will be run on the server. You can use the `parse_rest` client to call those functions.
The CloudCode guide describes how to upload a function to the server. Let's say you upload the following `main.js` script:
~~~~~ {javascript}
Parse.Cloud.define("hello", function(request, response) {
response.success("Hello world!");
});
Parse.Cloud.define("averageStars", function(request, response) {
var query = new Parse.Query("Review");
query.equalTo("movie", request.params.movie);
query.find({
success: function(results) {
var sum = 0;
for (var i = 0; i < results.length; ++i) {
sum += results[i].get("stars");
}
response.success(sum / results.length);
},
error: function() {
response.error("movie lookup failed");
}
});
});
~~~~~
Then you can call either of these functions using the `parse_rest.Function` class:
~~~~~ {python}
>>> hello_func = parse_rest.Function("hello")
>>> hello_func()
{u'result': u'Hello world!'}
>>> star_func = parse_rest.Function("averageStars")
>>> star_func(movie="The Matrix")
{u'result': 4.5}
~~~~~
That's it! This is a first try at a Python library for Parse, and is probably not bug-free. If you run into any issues, please get in touch -- dgrtwo@princeton.edu. Thanks! That's it! This is a first try at a Python library for Parse, and is probably not bug-free. If you run into any issues, please get in touch -- dgrtwo@princeton.edu. Thanks!
...@@ -37,7 +37,7 @@ class ParseBase(object): ...@@ -37,7 +37,7 @@ class ParseBase(object):
def execute(cls, uri, http_verb, extra_headers=None, **kw): def execute(cls, uri, http_verb, extra_headers=None, **kw):
headers = extra_headers or {} headers = extra_headers or {}
url = uri if uri.startswith(API_ROOT) else cls.ENDPOINT_ROOT + uri url = uri if uri.startswith(API_ROOT) else cls.ENDPOINT_ROOT + uri
data = kw and json.dumps(kw) or None data = kw and json.dumps(kw) or "{}"
if http_verb == 'GET' and data: if http_verb == 'GET' and data:
url += '?%s' % urllib.urlencode(kw) url += '?%s' % urllib.urlencode(kw)
data = None data = None
...@@ -113,6 +113,16 @@ class ParseBase(object): ...@@ -113,6 +113,16 @@ class ParseBase(object):
return (key, value) return (key, value)
class Function(ParseBase):
ENDPOINT_ROOT = "/".join((API_ROOT, "functions"))
def __init__(self, name):
self.name = name
def __call__(self, **kwargs):
return self.POST("/" + self.name, **kwargs)
class ParseResource(ParseBase): class ParseResource(ParseBase):
def __init__(self, **kw): def __init__(self, **kw):
self._object_id = kw.pop('objectId', None) self._object_id = kw.pop('objectId', None)
......
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("hello", function(request, response) {
response.success("Hello world!");
});
Parse.Cloud.define("averageStars", function(request, response) {
var query = new Parse.Query("Review");
query.equalTo("movie", request.params.movie);
query.find({
success: function(results) {
var sum = 0;
for (var i = 0; i < results.length; ++i) {
sum += results[i].get("stars");
}
response.success(sum / results.length);
},
error: function() {
response.error("movie lookup failed");
}
});
});
# Ignore everything in this directory
*
# Except this file
!.gitignore
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
Contains unit tests for the Python Parse REST API wrapper Contains unit tests for the Python Parse REST API wrapper
""" """
import os
import subprocess
import unittest import unittest
import urllib2 import urllib2
import datetime import datetime
...@@ -12,12 +14,30 @@ try: ...@@ -12,12 +14,30 @@ try:
import settings_local import settings_local
except ImportError: except ImportError:
raise ImportError('You must create a settings_local.py file with an ' + raise ImportError('You must create a settings_local.py file with an ' +
'example application to run tests') 'APPLICATION_ID, REST_API_KEY, and a MASTER_KEY ' +
'to run tests.')
parse_rest.APPLICATION_ID = settings_local.APPLICATION_ID parse_rest.APPLICATION_ID = settings_local.APPLICATION_ID
parse_rest.REST_API_KEY = settings_local.REST_API_KEY parse_rest.REST_API_KEY = settings_local.REST_API_KEY
GLOBAL_JSON_TEXT = """{
"applications": {
"_default": {
"link": "parseapi"
},
"parseapi": {
"applicationId": "%s",
"masterKey": "%s"
}
},
"global": {
"parseVersion": "1.1.16"
}
}
"""
### FUNCTIONS ### ### FUNCTIONS ###
def test_obj(saved=False): def test_obj(saved=False):
"""Return a test parse_rest.Object (content is from the docs)""" """Return a test parse_rest.Object (content is from the docs)"""
...@@ -141,6 +161,50 @@ class TestObjectAndQuery(unittest.TestCase): ...@@ -141,6 +161,50 @@ class TestObjectAndQuery(unittest.TestCase):
parse_rest.ObjectQuery("GameScore").get, obj_id) parse_rest.ObjectQuery("GameScore").get, obj_id)
class TestFunction(unittest.TestCase):
def setUp(self):
"""create and deploy cloud functions"""
original_dir = os.getcwd()
cloud_function_dir = os.path.join(os.path.split(__file__)[0],
"cloudcode")
os.chdir(cloud_function_dir)
# write the config file
with open("config/global.json", "w") as outf:
outf.write(GLOBAL_JSON_TEXT % (settings_local.APPLICATION_ID,
settings_local.MASTER_KEY))
try:
subprocess.call(["parse", "deploy"])
except OSError:
raise OSError("parse command line tool must be installed " +
"(see https://www.parse.com/docs/cloud_code_guide)")
os.chdir(original_dir)
# remove all existing Review objects
for review in parse_rest.ObjectQuery("Review").fetch():
review.delete()
def test_simple_functions(self):
"""test hello world and averageStars functions"""
# test the hello function- takes no arguments
hello_world_func = parse_rest.Function("hello")
ret = hello_world_func()
self.assertEqual(ret["result"], u"Hello world!")
# Test the averageStars function- takes simple argument
r1 = parse_rest.Object("Review", {"movie": "The Matrix",
"stars": 5,
"comment": "Too bad they never made any sequels."})
r1.save()
r2 = parse_rest.Object("Review", {"movie": "The Matrix",
"stars": 4,
"comment": "It's OK."})
r2.save()
star_func = parse_rest.Function("averageStars")
ret = star_func(movie="The Matrix")
self.assertAlmostEqual(ret["result"], 4.5)
if __name__ == "__main__": if __name__ == "__main__":
# command line # command line
unittest.main() unittest.main()
...@@ -14,10 +14,7 @@ class TestCommand(Command): ...@@ -14,10 +14,7 @@ class TestCommand(Command):
def run(self): def run(self):
"""Run test suite in parse_rest.tests""" """Run test suite in parse_rest.tests"""
try: from parse_rest import tests
from parse_rest import tests
except ImportError:
raise Exception("parse_rest is not installed, cannot run tests")
tests = TestLoader().loadTestsFromNames(["parse_rest.tests"]) tests = TestLoader().loadTestsFromNames(["parse_rest.tests"])
t = TextTestRunner(verbosity=1) t = TextTestRunner(verbosity=1)
t.run(tests) t.run(tests)
......
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