Skip to content

Commit

Permalink
Merge remote-tracking branch 'newsletterfilter/feature/formdata-post'…
Browse files Browse the repository at this point in the history
… into separate_serializers
  • Loading branch information
onlynone committed Nov 4, 2015
2 parents 4c4b7bc + 466f481 commit a5bb1f3
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 9 deletions.
25 changes: 18 additions & 7 deletions slumber/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class Resource(ResourceAttributesMixin, object):
def __init__(self, *args, **kwargs):
self._store = kwargs

def __call__(self, id=None, format=None, url_override=None):
def __call__(self, id=None, format=None, input_format=None, url_override=None):
"""
Returns a new instance of self modified by one or more of the available
parameters. These allows us to do things like override format for a
Expand All @@ -62,7 +62,7 @@ def __call__(self, id=None, format=None, url_override=None):
"""

# Short Circuit out if the call is empty
if id is None and format is None and url_override is None:
if id is None and format is None and input_format is None and url_override is None:
return self

kwargs = copy_kwargs(self._store)
Expand All @@ -73,6 +73,9 @@ def __call__(self, id=None, format=None, url_override=None):
if format is not None:
kwargs["format"] = format

if input_format is not None:
kwargs["input_format"] = input_format

if url_override is not None:
# @@@ This is hacky and we should probably figure out a better way
# of handling the case when a POST/PUT doesn't return an object
Expand All @@ -84,15 +87,18 @@ def __call__(self, id=None, format=None, url_override=None):
return self._get_resource(**kwargs)

def _request(self, method, data=None, files=None, params=None):
serializer = self._store["serializer"]
input_serializer = self._store["input_serializer"]
input_type = input_serializer.get_content_type()
output_serializer = self._store["serializer"]
output_type = output_serializer.get_content_type()
url = self.url()

headers = {"accept": serializer.get_content_type()}
headers = {"accept": output_type}

if not files:
headers["content-type"] = serializer.get_content_type()
headers["content-type"] = input_type
if data is not None:
data = serializer.dumps(data)
data = input_serializer.dumps(data)

resp = self._store["session"].request(method, url, data=data, params=params, files=files, headers=headers)

Expand Down Expand Up @@ -193,10 +199,13 @@ class API(ResourceAttributesMixin, object):

resource_class = Resource

def __init__(self, base_url=None, auth=None, format=None, append_slash=True, session=None, serializer=None):
def __init__(self, base_url=None, auth=None, format=None, input_format=None, append_slash=True, session=None, serializer=None, input_serializer=None):
if serializer is None:
serializer = Serializer(default=format)

if input_serializer is None:
input_serializer = Serializer(default=input_format)

if session is None:
session = requests.session()

Expand All @@ -206,9 +215,11 @@ def __init__(self, base_url=None, auth=None, format=None, append_slash=True, ses
self._store = {
"base_url": base_url,
"format": format if format is not None else "json",
"input_format": input_format if input_format is not None else "json",
"append_slash": append_slash,
"session": session,
"serializer": serializer,
"input_serializer": input_serializer,
}

# Do some Checks for Required Values
Expand Down
19 changes: 18 additions & 1 deletion slumber/serialize.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import urllib
import urlparse
from slumber import exceptions

_SERIALIZERS = {
"json": True,
"yaml": True,
"formdata": True,
}

try:
Expand Down Expand Up @@ -63,14 +66,28 @@ def dumps(self, data):
return yaml.dump(data)


class FormDataSerializer(BaseSerializer):

content_types = ['application/x-www-form-urlencoded']
key = "formdata"

def loads(self, data):
result = urlparse.parse_qs(data)
return result

def dumps(self, data):
result = urllib.urlencode(data)
return result


class Serializer(object):

def __init__(self, default=None, serializers=None):
if default is None:
default = "json" if _SERIALIZERS["json"] else "yaml"

if serializers is None:
serializers = [x() for x in [JsonSerializer, YamlSerializer] if _SERIALIZERS[x.key]]
serializers = [x() for x in [JsonSerializer, YamlSerializer, FormDataSerializer] if _SERIALIZERS[x.key]]

if not serializers:
raise exceptions.SerializerNoAvailable("There are no Available Serializers.")
Expand Down
20 changes: 19 additions & 1 deletion tests/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class ResourceTestCase(unittest.TestCase):

def setUp(self):
self.base_resource = slumber.Resource(base_url="http://example/api/v1/test", format="json", append_slash=False)
self.base_resource = slumber.Resource(base_url="http://example/api/v1/test", format="json", input_format="json", append_slash=False)

def test_get_200_json(self):
r = mock.Mock(spec=requests.Response)
Expand All @@ -23,6 +23,7 @@ def test_get_200_json(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -52,6 +53,7 @@ def test_get_200_text(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -81,6 +83,7 @@ def test_options_200_json(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -114,6 +117,7 @@ def test_head_200_json(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -149,6 +153,7 @@ def test_post_201_redirect(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.side_effect = (r1, r2)

Expand Down Expand Up @@ -178,6 +183,7 @@ def test_post_decodable_response(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -212,6 +218,7 @@ def test_patch_201_redirect(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.side_effect = (r1, r2)

Expand Down Expand Up @@ -241,6 +248,7 @@ def test_patch_decodable_response(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -275,6 +283,7 @@ def test_put_201_redirect(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.side_effect = (r1, r2)

Expand Down Expand Up @@ -304,6 +313,7 @@ def test_put_decodable_response(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand All @@ -327,6 +337,7 @@ def test_put_decodable_response(self):
def test_handle_serialization(self):
self.base_resource._store.update({
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})

resp = mock.Mock(spec=requests.Response)
Expand All @@ -348,6 +359,7 @@ def test_post_204_json(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})

self.base_resource._store["session"].request.return_value = resp
Expand All @@ -363,6 +375,7 @@ def test_get_200_subresource_json(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -396,6 +409,7 @@ def test_get_400_response(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})

self.base_resource._store["session"].request.return_value = r
Expand All @@ -413,6 +427,7 @@ def test_get_404_response(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand All @@ -428,6 +443,7 @@ def test_get_500_response(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -474,6 +490,7 @@ def test_get_200_json_py3(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down Expand Up @@ -523,6 +540,7 @@ def test_unicode_decodable_response(self):
self.base_resource._store.update({
"session": mock.Mock(spec=requests.Session),
"serializer": slumber.serialize.Serializer(),
"input_serializer": slumber.serialize.Serializer(),
})
self.base_resource._store["session"].request.return_value = r

Expand Down
21 changes: 21 additions & 0 deletions tests/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,24 @@ def test_yaml_get_serializer(self):
result = serializer.dumps(self.data)
self.assertEqual(result, "{foo: bar}\n")
self.assertEqual(self.data, serializer.loads(result))

def test_formdata_get_serializer(self):
s = slumber.serialize.Serializer()

serializer = None
for content_type in [
"application/x-www-form-urlencoded",
]:
serializer = s.get_serializer(content_type=content_type)
self.assertEqual(type(serializer), slumber.serialize.FormDataSerializer,
"content_type %s should produce a FormDataSerializer")

result = serializer.dumps(self.data)
self.assertEqual(result, "foo=bar")

# The following would currently fail because parsing a query string
# leads to the values being lists instead of single items so you get:
#
# {'foo': ['bar']}
#
# self.assertEqual(self.data, serializer.loads(result))

0 comments on commit a5bb1f3

Please sign in to comment.