diff --git a/pillarsdk/utils.py b/pillarsdk/utils.py index a56a3ad..57d6b93 100644 --- a/pillarsdk/utils.py +++ b/pillarsdk/utils.py @@ -51,15 +51,15 @@ def join_url_params(url, params): def convert_to_string(param): if isinstance(param, dict): - return json.dumps(param) + return json.dumps(param, sort_keys=True) if isinstance(param, text_type): return param.encode('utf-8') return param - jsonified_params = { - key: convert_to_string(param) - for key, param in params.items() - } + # Pass as (key, value) pairs, so that the sorted order is maintained. + jsonified_params = [ + (key, convert_to_string(params[key])) + for key in sorted(params.keys())] return url + "?" + urlencode(jsonified_params) diff --git a/tests/test_utils.py b/tests/test_utils.py index 3d7abad..96c86d8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -42,6 +42,17 @@ class PillarUtilsTests(unittest.TestCase): self.assertEqual('url?dict=' + quote_plus(r'{"food": "\u0e1c\u0e31\u0e14\u0e44\u0e17\u0e22"}'), utils.join_url_params('url', {'dict': {'food': 'ผัดไทย'}})) + def test_join_url_params_sorting(self): + # Different sorting than in the tests before, and using multiple keys per dict so + # that sorting is actually relevant. + self.assertEqual('url?after=haha+actually+before' + + '&dict=' + quote_plus(r'{"drinks": "water", "food": "\u0e1c\u0e31\u0e14\u0e44\u0e17\u0e22"}') + + '&last=yeah%2C+last', + utils.join_url_params('url', {'dict': {'food': 'ผัดไทย', 'drinks': 'water'}, + 'after': 'haha actually before', + 'last': 'yeah, last'})) + + def test_merge_dict(self): self.assertEqual({1: 2, 'foo': 'bar', 'foo1': 'bar2'}, utils.merge_dict({"foo": "bar"}, {1: 2}, {"foo1": "bar2"}))