diff --git a/pillarsdk/utils.py b/pillarsdk/utils.py index 8e59c52..d7d36a9 100644 --- a/pillarsdk/utils.py +++ b/pillarsdk/utils.py @@ -1,4 +1,6 @@ +import json import re +import six from datetime import datetime try: @@ -13,8 +15,8 @@ def join_url(url, *paths): Usage:: - >>> utils.join_url("pillar:5000", "shots") - pillar:5000/shots + >>> join_url("pillar:5000", "shots") + 'pillar:5000/shots' """ for path in paths: url = re.sub(r'/?$', re.sub(r'^/?', '/', path), url) @@ -26,10 +28,25 @@ def join_url_params(url, params): Usage:: - >>> utils.join_url_params("pillar:5000/shots", {"page-id": 2, "NodeType": "Shot Group"}) - pillar:5000/shots?page-id=2&NodeType=Shot+Group + >>> join_url_params("pillar:5000/shots", {"page-id": 2, "NodeType": "Shot Group"}) + 'pillar:5000/shots?page-id=2&NodeType=Shot+Group' """ - return url + "?" + urlencode(params) + + if params is None: + return url + + def convert_to_string(param): + if isinstance(param, dict): + return json.dumps(param) + if isinstance(param, six.text_type): + return param.encode('utf-8') + return param + + jsonified_params = { + key: convert_to_string(param) + for key, param in params.items() + } + return url + "?" + urlencode(jsonified_params) def merge_dict(data, *override): @@ -38,8 +55,9 @@ def merge_dict(data, *override): Usage:: - >>> utils.merge_dict({"foo": "bar"}, {1: 2}, {"foo1": "bar2"}) - {1: 2, 'foo': 'bar', 'foo1': 'bar2'} + >>> md = merge_dict({"foo": "bar"}, {1: 2}, {"foo1": "bar2"}) + >>> md == {1: 2, 'foo': 'bar', 'foo1': 'bar2'} + True """ result = {} for current_dict in (data,) + override: diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..5c496c2 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,43 @@ +# -*- encoding: utf-8 -*- + +import unittest + +try: + from urllib.parse import quote_plus +except ImportError: + # Python 2 + from urllib import quote_plus + +from pillarsdk import utils + + +class PillarUtilsTests(unittest.TestCase): + + def test_join_url_params(self): + """Test that strings and dicts work as parameters.""" + + # Test empty and None parameters + self.assertEqual('?', utils.join_url_params('', {})) + self.assertEqual('url', utils.join_url_params('url', None)) + + # Test simple string values + self.assertEqual('url?param=simple_param', utils.join_url_params('url', {'param': 'simple_param'})) + self.assertEqual('url?param=space+param', utils.join_url_params('url', {'param': 'space param'})) + + # Test dictionary + self.assertEqual('url?dict=' + quote_plus('{"key": "value"}'), + utils.join_url_params('url', {'dict': {'key': 'value'}})) + + # Test nested dictionary + self.assertEqual('url?dict=' + quote_plus('{"key": {"subkey": "subvalue"}}'), + utils.join_url_params('url', {'dict': {'key': {'subkey': 'subvalue'}}})) + + def test_join_url_params_encoding(self): + """Test that unicode objects in the query parameters are properly UTF-8 encoded.""" + + # Test simple unicode string + self.assertEqual('url?param=St%C3%BCvel', utils.join_url_params('url', {'param': u'Stüvel'})) + + # Test unicode value in string + self.assertEqual('url?dict=' + quote_plus(r'{"food": "\u0e1c\u0e31\u0e14\u0e44\u0e17\u0e22"}'), + utils.join_url_params('url', {'dict': {'food': 'ผัดไทย'}}))