diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/array_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/array_tests.py index 9b14f19b39669..0b9a56ff2c9f8 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/array_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/array_tests.py @@ -208,6 +208,9 @@ def test_array(self): with self.assertRaises(DeserializationError): client.array.get_date_time_invalid_chars() + test_array = ['a string that gets encoded with base64url', 'test string', 'Lorem ipsum'] + self.assertEqual(client.array.get_base64_url(), test_array) + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/custom_base_uri_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/custom_base_uri_tests.py index 38c16c2a7a147..d1a49930ec386 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/custom_base_uri_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/custom_base_uri_tests.py @@ -39,6 +39,7 @@ tests = realpath(join(cwd, pardir, "Expected", "AcceptanceTests")) sys.path.append(join(tests, "CustomBaseUri")) +sys.path.append(join(tests, "CustomBaseUriMoreOptions")) from msrest.exceptions import ( DeserializationError, @@ -48,6 +49,7 @@ from autorestparameterizedhosttestclient import AutoRestParameterizedHostTestClient from autorestparameterizedhosttestclient.models import Error, ErrorException +from autorestparameterizedcustomhosttestclient import AutoRestParameterizedCustomHostTestClient class CustomBaseUriTests(unittest.TestCase): @@ -70,6 +72,10 @@ def test_custom_base_uri_negative(self): with self.assertRaises(ClientRequestError): client.paths.get_empty("local") + def test_custom_base_uri_more_optiopns(self): + client = AutoRestParameterizedCustomHostTestClient("test12", "host.:3000") + client.paths.get_empty("http://lo", "cal", "key1") + if __name__ == '__main__': diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/dictionary_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/dictionary_tests.py index fdb8cc180c834..a3dc4cef36f45 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/dictionary_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/dictionary_tests.py @@ -182,6 +182,11 @@ def test_dictionary_primitive_types(self): bytes_result = self.client.dictionary.get_byte_invalid_null() self.assertEqual(bytes_null, bytes_result) + test_dict = {'0': 'a string that gets encoded with base64url', + '1': 'test string', + '2': 'Lorem ipsum'} + self.assertEqual(self.client.dictionary.get_base64_url(), test_dict) + def test_basic_dictionary_parsing(self): self.assertEqual({}, self.client.dictionary.get_empty()) diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/string_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/string_tests.py index 6c4e1e2ca5af1..c012b3e9407cd 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/string_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/string_tests.py @@ -104,6 +104,11 @@ def test_string(self): self.assertEqual(Colors.redcolor, client.enum.get_not_expandable()) client.enum.put_not_expandable(Colors.redcolor) + self.assertEqual(client.string.get_base64_encoded(), 'a string that gets encoded with base64') + self.assertEqual(client.string.get_base64_url_encoded(), 'a string that gets encoded with base64url') + self.assertIsNone(client.string.get_null_base64_url_encoded()) + client.string.put_base64_url_encoded('a string that gets encoded with base64url') + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/url_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/url_tests.py index 819725c37854f..9eee73e422f22 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/url_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/url_tests.py @@ -101,6 +101,8 @@ def test_url_path(self): with self.assertRaises(ValidationError): self.client.paths.enum_null(None) + self.client.paths.base64_url("lorem") + def test_url_query(self): self.client.config.global_string_path = '' diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py index 1d437ec67f879..cf60a316ab363 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyArray/autorestswaggerbatarrayservice/operations/array.py @@ -2090,7 +2090,7 @@ def get_base64_url( deserialized = None if response.status_code == 200: - deserialized = self._deserialize('[str]', response) + deserialized = self._deserialize('[base64]', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyDictionary/autorestswaggerbatdictionaryservice/operations/dictionary.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyDictionary/autorestswaggerbatdictionaryservice/operations/dictionary.py index 55fd8ef1953dc..ef93fea23fd68 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyDictionary/autorestswaggerbatdictionaryservice/operations/dictionary.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyDictionary/autorestswaggerbatdictionaryservice/operations/dictionary.py @@ -2095,7 +2095,7 @@ def get_base64_url( deserialized = None if response.status_code == 200: - deserialized = self._deserialize('{str}', response) + deserialized = self._deserialize('{base64}', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyString/autorestswaggerbatservice/operations/string.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyString/autorestswaggerbatservice/operations/string.py index 5910495329f88..854f52c97752b 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyString/autorestswaggerbatservice/operations/string.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyString/autorestswaggerbatservice/operations/string.py @@ -473,7 +473,7 @@ def get_base64_encoded( deserialized = None if response.status_code == 200: - deserialized = self._deserialize('str', response) + deserialized = self._deserialize('base64', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) @@ -517,7 +517,7 @@ def get_base64_url_encoded( deserialized = None if response.status_code == 200: - deserialized = self._deserialize('str', response) + deserialized = self._deserialize('base64', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) @@ -554,7 +554,7 @@ def put_base64_url_encoded( header_parameters.update(custom_headers) # Construct body - body_content = self._serialize.body(string_body, 'str') + body_content = self._serialize.body(string_body, 'base64') # Construct and send request request = self._client.put(url, query_parameters) @@ -604,7 +604,7 @@ def get_null_base64_url_encoded( deserialized = None if response.status_code == 200: - deserialized = self._deserialize('str', response) + deserialized = self._deserialize('base64', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Url/autoresturltestservice/operations/paths.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Url/autoresturltestservice/operations/paths.py index 3e6e638a031b2..c0e56ad997211 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Url/autoresturltestservice/operations/paths.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Url/autoresturltestservice/operations/paths.py @@ -1046,7 +1046,7 @@ def base64_url( # Construct URL url = '/paths/string/bG9yZW0/{base64UrlPath}' path_format_arguments = { - 'base64UrlPath': self._serialize.url("base64_url_path", base64_url_path, 'str') + 'base64UrlPath': self._serialize.url("base64_url_path", base64_url_path, 'base64') } url = self._client.format_url(url, **path_format_arguments) diff --git a/AutoRest/Generators/Python/Python/ClientModelExtensions.cs b/AutoRest/Generators/Python/Python/ClientModelExtensions.cs index 330d8aef564c6..00622b4d9e1a3 100644 --- a/AutoRest/Generators/Python/Python/ClientModelExtensions.cs +++ b/AutoRest/Generators/Python/Python/ClientModelExtensions.cs @@ -152,6 +152,11 @@ public static string ToPythonRuntimeTypeString(this IType type) { return "unix-time"; } + + if (known.Type == KnownPrimaryType.Base64Url) + { + return "base64"; + } } var sequenceType = type as SequenceType; @@ -252,7 +257,8 @@ public static string GetPythonSerializationType(IType type) { KnownPrimaryType.DateTime, "iso-8601" }, { KnownPrimaryType.DateTimeRfc1123, "rfc-1123" }, { KnownPrimaryType.TimeSpan, "duration" }, - { KnownPrimaryType.UnixTime, "unix-time" } + { KnownPrimaryType.UnixTime, "unix-time" }, + { KnownPrimaryType.Base64Url, "base64" } }; PrimaryType primaryType = type as PrimaryType; if (primaryType != null) diff --git a/AutoRest/TestServer/server/app.js b/AutoRest/TestServer/server/app.js index 9453e73965c22..a462baa91787d 100644 --- a/AutoRest/TestServer/server/app.js +++ b/AutoRest/TestServer/server/app.js @@ -419,7 +419,7 @@ var coverage = { "ConstantsInPath": 0, "ConstantsInBody": 0, "CustomBaseUri": 0, - //Once all the languages implement this test, the scenario counter should be reset to zero. It is currently implemented in C# and node.js + //Once all the languages implement this test, the scenario counter should be reset to zero. It is currently implemented in C#, Python and node.js "CustomBaseUriMoreOptions": 1, 'getModelFlattenArray': 0, 'putModelFlattenArray': 0, @@ -430,7 +430,7 @@ var coverage = { 'putModelFlattenCustomBase': 0, 'postModelFlattenCustomParameter': 0, 'putModelFlattenCustomGroupedParameter': 0, - /* TODO: only C# and node.js support the base64url format currently. Exclude these tests from code coverage until it is implemented in other languages */ + /* TODO: only C#, Python and node.js support the base64url format currently. Exclude these tests from code coverage until it is implemented in other languages */ "getStringBase64Encoded": 1, "getStringBase64UrlEncoded": 1, "putStringBase64UrlEncoded": 1, @@ -439,7 +439,7 @@ var coverage = { "getDictionaryBase64Url": 1, "UrlPathsStringBase64Url": 1, "UrlPathsArrayCSVInPath": 1, - /* TODO: only C# supports the unixtime format currently. Exclude these tests from code coverage until it is implemented in other languages */ + /* TODO: only C# and Python support the unixtime format currently. Exclude these tests from code coverage until it is implemented in other languages */ "getUnixTime": 1, "getInvalidUnixTime": 1, "getNullUnixTime": 1, diff --git a/ClientRuntimes/Python/msrest/msrest/serialization.py b/ClientRuntimes/Python/msrest/msrest/serialization.py index 8ac7fcb82385d..ef16b011e4b7d 100644 --- a/ClientRuntimes/Python/msrest/msrest/serialization.py +++ b/ClientRuntimes/Python/msrest/msrest/serialization.py @@ -205,6 +205,7 @@ def __init__(self, classes=None): 'decimal': Serializer.serialize_decimal, 'long': Serializer.serialize_long, 'bytearray': Serializer.serialize_bytearray, + 'base64': Serializer.serialize_base64, 'object': self.serialize_object, '[]': self.serialize_iter, '{}': self.serialize_dict @@ -541,6 +542,16 @@ def serialize_bytearray(attr, **kwargs): """ return b64encode(attr).decode() + @staticmethod + def serialize_base64(attr, **kwargs): + """Serialize str into base-64 string. + + :param attr: Object to be serialized. + :rtype: str + """ + encoded = b64encode(attr.encode()).decode() + return encoded.strip('=').replace('+', '-').replace('/', '_') + @staticmethod def serialize_decimal(attr, **kwargs): """Serialize Decimal object to float. @@ -675,6 +686,7 @@ def __init__(self, classes=None): 'decimal': Deserializer.deserialize_decimal, 'long': Deserializer.deserialize_long, 'bytearray': Deserializer.deserialize_bytearray, + 'base64': Deserializer.deserialize_base64, 'object': self.deserialize_object, '[]': self.deserialize_iter, '{}': self.deserialize_dict @@ -985,6 +997,20 @@ def deserialize_bytearray(attr): """ return bytearray(b64decode(attr)) + @staticmethod + def deserialize_base64(attr): + """Deserialize base64 encoded string into string. + + :param str attr: response string to be deserialized. + :rtype: bytearray + :raises: TypeError if string format invalid. + """ + pad_count = 3 - (len(attr) + 3) % 4 + padding = ['='] * pad_count + attr = attr + ''.join(padding) + encoded = attr.replace('-', '+').replace('_', '/') + return b64decode(encoded).decode() + @staticmethod def deserialize_decimal(attr): """Deserialize string into Decimal object.