diff --git a/SoftLayer/transports.py b/SoftLayer/transports.py index 297249852..02cd7a214 100644 --- a/SoftLayer/transports.py +++ b/SoftLayer/transports.py @@ -5,6 +5,7 @@ :license: MIT, see LICENSE for more details. """ +import base64 import importlib import json import logging @@ -359,7 +360,7 @@ def __call__(self, request): body['parameters'] = request.args if body: - request.payload = json.dumps(body) + request.payload = json.dumps(body, cls=ComplexEncoder) url_parts = [self.endpoint_url, request.service] if request.identifier is not None: @@ -566,3 +567,17 @@ def _format_object_mask(objectmask): not objectmask.startswith('[')): objectmask = "mask[%s]" % objectmask return objectmask + + +class ComplexEncoder(json.JSONEncoder): + """ComplexEncoder helps jsonencoder deal with byte strings""" + + def default(self, o): + """Encodes o as JSON""" + + # Base64 encode bytes type objects. + if isinstance(o, bytes): + base64_bytes = base64.b64encode(o) + return base64_bytes.decode("utf-8") + # Let the base class default method raise the TypeError + return json.JSONEncoder.default(self, o) diff --git a/tests/transport_tests.py b/tests/transport_tests.py index d105c3fdc..6e6c793fd 100644 --- a/tests/transport_tests.py +++ b/tests/transport_tests.py @@ -7,6 +7,7 @@ import io import warnings +import json import mock import pytest import requests @@ -527,6 +528,30 @@ def test_with_args(self, request): proxies=None, timeout=None) + @mock.patch('SoftLayer.transports.requests.Session.request') + def test_with_args_bytes(self, request): + request().text = '{}' + + req = transports.Request() + req.service = 'SoftLayer_Service' + req.method = 'getObject' + req.args = ('test', b'asdf') + + resp = self.transport(req) + + self.assertEqual(resp, {}) + request.assert_called_with( + 'POST', + 'http://something.com/SoftLayer_Service/getObject.json', + headers=mock.ANY, + auth=None, + data='{"parameters": ["test", "YXNkZg=="]}', + params={}, + verify=True, + cert=None, + proxies=None, + timeout=None) + @mock.patch('SoftLayer.transports.requests.Session.request') def test_with_filter(self, request): request().text = '{}' @@ -674,6 +699,16 @@ def test_print_reproduceable(self): output_text = self.transport.print_reproduceable(req) self.assertIn("https://test.com", output_text) + def test_complex_encoder_bytes(self): + to_encode = { + 'test': ['array', 0, 1, False], + 'bytes': b'ASDASDASD' + } + result = json.dumps(to_encode, cls=transports.ComplexEncoder) + # result = '{"test": ["array", 0, 1, false], "bytes": "QVNEQVNEQVNE"}' + # encode doesn't always encode in the same order, so testing exact match SOMETIMES breaks. + self.assertIn("QVNEQVNEQVNE", result) + class TestFixtureTransport(testing.TestCase):