Skip to content
This repository has been archived by the owner on Dec 8, 2023. It is now read-only.

Adds JSON body parsing to Request handling #20

Merged
merged 1 commit into from
Sep 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pale/adapters/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ def bind_blueprint(pale_api_module, flask_blueprint):
class DefaultFlaskContext(pale.context.DefaultContext):

def build_args_from_request(self, request):
return request.values.to_dict(flat=False)
body = request.get_data(as_text=True)

qs_args = request.values.to_dict(flat=False)
js_args = self.deserialize_args_from_body(body)

qs_args.update(js_args)
return qs_args

def __init__(self, endpoint, request):
super(DefaultFlaskContext, self).__init__()
Expand Down
10 changes: 10 additions & 0 deletions pale/adapters/webapp2.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,19 @@ def build_args_from_request(self, request):
keys = request.arguments()
args = {}

j_args = self.deserialize_args_from_body(request.body)

for key in keys:
args[key] = request.get_all(key)

# json/body args
for k,v in j_args.iteritems():
if k in args:
logging.warning("Overwriting request arg %s with json arg",
k)
args[k] = v

# route args
for k,v in request.route_kwargs.iteritems():
if k in args:
logging.warning("Overwriting request arg %s with route arg",
Expand Down
22 changes: 22 additions & 0 deletions pale/context.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import logging
import json

json_decoder = json.JSONDecoder()

class DefaultContext(object):
"""A default Context object for pale request data"""
request = None
Expand All @@ -14,3 +19,20 @@ class DefaultContext(object):

handler_result = None
response = None

@classmethod
def deserialize_args_from_body(cls, body):
"""Deserializes a JSON body into a dictionary of arguments."""

if not body:
return {}

# Add arguments from json body
try:
j_args = json_decoder.decode(body)
return j_args
except:
logging.exception(
"Failed to parse arguments from JSON body.\n%s",
body)
return {}
5 changes: 3 additions & 2 deletions pale/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ def _parse_args(self):
# be a list, which we strip out if the length is 1 and if the
# validator doesn't expect a list
if patched_value is not None and \
isinstance(patched_value, list) and \
len(patched_value) == 1 and \
list not in arg_obj.allowed_types:
patched_value = patched_value[0]
Expand All @@ -259,10 +260,10 @@ def _parse_args(self):

def _parse_handler_result(self, result):
"""Parses the item(s) returned by your handler implementation.

Handlers may return a single item (payload), or a tuple that gets
passed to the Response class __init__ method of your HTTP layer.

_parse_handler_result separates the payload from the rest the tuple,
as well as providing the tuple so that it can be re-composed after
the payload has been run through the `_returns` Resource's renderer.
Expand Down
19 changes: 19 additions & 0 deletions tests/test_flask_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,31 @@ def test_successful_post_with_required_params(self):
self.assertExpectedFields(returned_time, expected_fields)


def test_successful_json_post_with_required_params(self):
# month is required in the endpoint definition, so we must pass
# it in here
resp = self.app.post_json('/api/time/parse', {'month': 2})

self.assertEqual(resp.status_code, 200)
self.assertIn('time', resp.json_body)

returned_time = resp.json_body['time']

# we didn't specify any other fields in the endpoint definition,
# so this one should only get the defaults
expected_fields = DateTimeResource._default_fields

self.assertExpectedFields(returned_time, expected_fields)


def test_unsuccessful_post_missing_required_params(self):
resp = self.app.post('/api/time/parse', status=422)

self.assertIn('error', resp.json_body)




def test_getting_with_nested_resources(self):
test_duration = 60 * 1000 # one minute in milliseconds
resp = self.app.get('/api/time/range', {'duration': test_duration})
Expand Down